/*
 * Linux cfgp2p driver
 *
 * Copyright (C) 1999-2019, Broadcom.
 *
 *      Unless you and Broadcom execute a separate written software license
 * agreement governing use of this software, this software is licensed to you
 * under the terms of the GNU General Public License version 2 (the "GPL"),
 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
 * following added to such license:
 *
 *      As a special exception, the copyright holders of this software give you
 * permission to link this software with independent modules, and to copy and
 * distribute the resulting executable under terms of your choice, provided that
 * you also meet, for each linked independent module, the terms and conditions
 * of the license of that module.  An independent module is a module which is
 * not derived from this software.  The special exception does not apply to any
 * modifications of the software.
 *
 *      Notwithstanding the above, under no circumstances may you combine this
 * software in any way with any other Broadcom software provided under a license
 * other than the GPL, without Broadcom's express prior written consent.
 *
 *
 * <<Broadcom-WL-IPTag/Open:>>
 *
 * $Id: wl_cfgp2p.c 819430 2019-05-13 11:38:06Z $
 *
 */
#include <typedefs.h>
#include <linuxver.h>
#include <osl.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/if_arp.h>
#include <asm/uaccess.h>

#include <bcmutils.h>
#include <bcmstdlib_s.h>
#include <bcmendian.h>
#include <ethernet.h>
#include <802.11.h>
#include <net/rtnetlink.h>

#include <wl_cfg80211.h>
#include <wl_cfgp2p.h>
#include <wl_cfgscan.h>
#include <wldev_common.h>
#include <wl_ohos.h>
#include <dngl_stats.h>
#include <dhd.h>
#include <dhd_linux.h>
#include <dhdioctl.h>
#include <wlioctl.h>
#include <dhd_cfg80211.h>
#include <dhd_bus.h>
#include <dhd_config.h>

static s8 scanparambuf[WLC_IOCTL_SMLEN];
static bool wl_cfgp2p_has_ie(const bcm_tlv_t *ie, const u8 **tlvs,
                             u32 *tlvs_len, const u8 *oui, u32 oui_len,
                             u8 type);

static s32 wl_cfgp2p_cancel_listen(struct bcm_cfg80211 *cfg,
                                   struct net_device *ndev,
                                   struct wireless_dev *wdev, bool notify);

#if defined(WL_ENABLE_P2P_IF)
static netdev_tx_t wl_cfgp2p_start_xmit(struct sk_buff *skb,
                                        struct net_device *ndev);
static int wl_cfgp2p_do_ioctl(struct net_device *net, struct ifreq *ifr,
                              int cmd);
static int wl_cfgp2p_if_open(struct net_device *net);
static int wl_cfgp2p_if_stop(struct net_device *net);

static const struct net_device_ops wl_cfgp2p_if_ops = {
    .ndo_open = wl_cfgp2p_if_open,
    .ndo_stop = wl_cfgp2p_if_stop,
    .ndo_do_ioctl = wl_cfgp2p_do_ioctl,
    .ndo_start_xmit = wl_cfgp2p_start_xmit,
};
#endif /* WL_ENABLE_P2P_IF */

#ifdef CONFIG_AP6XXX_WIFI6_HDF
#include "net_device.h"

extern int g_event_ifidx;
struct NetDevice *get_hdf_netdev(int ifidx);
int32_t HdfWifiEventCancelRemainOnChannel(const struct NetDevice *netDev,
                                          uint32_t freq);
#endif

int dhd_del_monitor(struct net_device *ndev);
int magiclink_add_p2p(const char *name, struct ether_addr *p2p_dev_addr,
                      struct net_device **new_ndev);
struct net_device *p2p_debug_ndev = NULL;

bool wl_cfgp2p_is_pub_action(void *frame, u32 frame_len)
{
    wifi_p2p_pub_act_frame_t *pact_frm;

    if (frame == NULL) {
        return false;
    }
    pact_frm = (wifi_p2p_pub_act_frame_t *)frame;
    if (frame_len < sizeof(wifi_p2p_pub_act_frame_t) - 1) {
        return false;
    }

    if (pact_frm->category == P2P_PUB_AF_CATEGORY &&
        pact_frm->action == P2P_PUB_AF_ACTION &&
        pact_frm->oui_type == P2P_VER &&
        memcmp(pact_frm->oui, P2P_OUI, sizeof(pact_frm->oui)) == 0) {
        return true;
    }

    return false;
}

bool wl_cfgp2p_is_p2p_action(void *frame, u32 frame_len)
{
    wifi_p2p_action_frame_t *act_frm;

    if (frame == NULL) {
        return false;
    }
    act_frm = (wifi_p2p_action_frame_t *)frame;
    if (frame_len < sizeof(wifi_p2p_action_frame_t) - 1) {
        return false;
    }

    if (act_frm->category == P2P_AF_CATEGORY && act_frm->type == P2P_VER &&
        memcmp(act_frm->OUI, P2P_OUI, DOT11_OUI_LEN) == 0) {
        return true;
    }

    return false;
}

#define GAS_RESP_LEN 2
#define DOUBLE_TLV_BODY_OFF 4
#define GAS_RESP_OFFSET 4
#define GAS_CRESP_OFFSET 5

bool wl_cfgp2p_find_gas_subtype(u8 subtype, u8 *data, u32 len)
{
    const bcm_tlv_t *ie = (bcm_tlv_t *)data;
    const u8 *frame = NULL;
    u16 id, flen;

    /* Skipped first ANQP Element, if frame has anqp elemnt */
    ie = bcm_parse_tlvs(ie, len, DOT11_MNG_ADVERTISEMENT_ID);
    if (ie == NULL) {
        return false;
    }

    frame = (const uint8 *)ie + ie->len + TLV_HDR_LEN + GAS_RESP_LEN;
    id = ((u16)(((frame)[1] << 0x8) | (frame)[0]));
    flen = ((u16)(((frame)[0x3] << 0x8) | (frame)[0x2]));

    /* If the contents match the OUI and the type */
    if (flen >= WFA_OUI_LEN + 1 && id == P2PSD_GAS_NQP_INFOID &&
        !bcmp(&frame[DOUBLE_TLV_BODY_OFF], (const uint8 *)WFA_OUI,
              WFA_OUI_LEN) &&
        subtype == frame[DOUBLE_TLV_BODY_OFF + WFA_OUI_LEN]) {
        return true;
    }

    return false;
}

bool wl_cfgp2p_is_gas_action(void *frame, u32 frame_len)
{
    wifi_p2psd_gas_pub_act_frame_t *sd_act_frm;

    if (frame == NULL) {
        return false;
    }

    sd_act_frm = (wifi_p2psd_gas_pub_act_frame_t *)frame;
    if (frame_len < (sizeof(wifi_p2psd_gas_pub_act_frame_t) - 1)) {
        return false;
    }
    if (sd_act_frm->category != P2PSD_ACTION_CATEGORY) {
        return false;
    }

#ifdef WL11U
    if (sd_act_frm->action == P2PSD_ACTION_ID_GAS_IRESP) {
        return wl_cfgp2p_find_gas_subtype(
            P2PSD_GAS_OUI_SUBTYPE,
            (u8 *)sd_act_frm->query_data + GAS_RESP_OFFSET, frame_len);
    } else if (sd_act_frm->action == P2PSD_ACTION_ID_GAS_CRESP) {
        return wl_cfgp2p_find_gas_subtype(
            P2PSD_GAS_OUI_SUBTYPE,
            (u8 *)sd_act_frm->query_data + GAS_CRESP_OFFSET, frame_len);
    } else if (sd_act_frm->action == P2PSD_ACTION_ID_GAS_IREQ ||
               sd_act_frm->action == P2PSD_ACTION_ID_GAS_CREQ) {
        return true;
    } else {
        return false;
    }
#else
    if (sd_act_frm->action == P2PSD_ACTION_ID_GAS_IREQ ||
        sd_act_frm->action == P2PSD_ACTION_ID_GAS_IRESP ||
        sd_act_frm->action == P2PSD_ACTION_ID_GAS_CREQ ||
        sd_act_frm->action == P2PSD_ACTION_ID_GAS_CRESP) {
        return true;
    } else {
        return false;
    }
#endif /* WL11U */
}

bool wl_cfgp2p_is_p2p_gas_action(void *frame, u32 frame_len)
{
    wifi_p2psd_gas_pub_act_frame_t *sd_act_frm;

    if (frame == NULL) {
        return false;
    }

    sd_act_frm = (wifi_p2psd_gas_pub_act_frame_t *)frame;
    if (frame_len < (sizeof(wifi_p2psd_gas_pub_act_frame_t) - 1)) {
        return false;
    }
    if (sd_act_frm->category != P2PSD_ACTION_CATEGORY) {
        return false;
    }

    if (sd_act_frm->action == P2PSD_ACTION_ID_GAS_IREQ) {
        return wl_cfgp2p_find_gas_subtype(
            P2PSD_GAS_OUI_SUBTYPE, (u8 *)sd_act_frm->query_data, frame_len);
    } else {
        return false;
    }
}

void wl_cfgp2p_print_actframe(bool tx, void *frame, u32 frame_len, u32 channel)
{
    wifi_p2p_pub_act_frame_t *pact_frm;
    wifi_p2p_action_frame_t *act_frm;
    wifi_p2psd_gas_pub_act_frame_t *sd_act_frm;
    if (!frame || frame_len <= 0x2) {
        return;
    }

    if (wl_cfgp2p_is_pub_action(frame, frame_len)) {
        pact_frm = (wifi_p2p_pub_act_frame_t *)frame;
        switch (pact_frm->subtype) {
            case P2P_PAF_GON_REQ:
                CFGP2P_ACTION(("%s P2P Group Owner Negotiation Req Frame,"
                               " channel=%d\n",
                               (tx) ? "TX" : "RX", channel));
                break;
            case P2P_PAF_GON_RSP:
                CFGP2P_ACTION(("%s P2P Group Owner Negotiation Rsp Frame,"
                               " channel=%d\n",
                               (tx) ? "TX" : "RX", channel));
                break;
            case P2P_PAF_GON_CONF:
                CFGP2P_ACTION(("%s P2P Group Owner Negotiation Confirm Frame,"
                               " channel=%d\n",
                               (tx) ? "TX" : "RX", channel));
                break;
            case P2P_PAF_INVITE_REQ:
                CFGP2P_ACTION(("%s P2P Invitation Request  Frame,"
                               " channel=%d\n",
                               (tx) ? "TX" : "RX", channel));
                break;
            case P2P_PAF_INVITE_RSP:
                CFGP2P_ACTION(("%s P2P Invitation Response Frame,"
                               " channel=%d\n",
                               (tx) ? "TX" : "RX", channel));
                break;
            case P2P_PAF_DEVDIS_REQ:
                CFGP2P_ACTION(("%s P2P Device Discoverability Request Frame,"
                               " channel=%d\n",
                               (tx) ? "TX" : "RX", channel));
                break;
            case P2P_PAF_DEVDIS_RSP:
                CFGP2P_ACTION(("%s P2P Device Discoverability Response Frame,"
                               " channel=%d\n",
                               (tx) ? "TX" : "RX", channel));
                break;
            case P2P_PAF_PROVDIS_REQ:
                CFGP2P_ACTION(("%s P2P Provision Discovery Request Frame,"
                               " channel=%d\n",
                               (tx) ? "TX" : "RX", channel));
                break;
            case P2P_PAF_PROVDIS_RSP:
                CFGP2P_ACTION(("%s P2P Provision Discovery Response Frame,"
                               " channel=%d\n",
                               (tx) ? "TX" : "RX", channel));
                break;
            default:
                CFGP2P_ACTION(("%s Unknown Public Action Frame,"
                               " channel=%d\n",
                               (tx) ? "TX" : "RX", channel));
        }
    } else if (wl_cfgp2p_is_p2p_action(frame, frame_len)) {
        act_frm = (wifi_p2p_action_frame_t *)frame;
        switch (act_frm->subtype) {
            case P2P_AF_NOTICE_OF_ABSENCE:
                CFGP2P_ACTION(("%s P2P Notice of Absence Frame,"
                               " channel=%d\n",
                               (tx) ? "TX" : "RX", channel));
                break;
            case P2P_AF_PRESENCE_REQ:
                CFGP2P_ACTION(("%s P2P Presence Request Frame,"
                               " channel=%d\n",
                               (tx) ? "TX" : "RX", channel));
                break;
            case P2P_AF_PRESENCE_RSP:
                CFGP2P_ACTION(("%s P2P Presence Response Frame,"
                               " channel=%d\n",
                               (tx) ? "TX" : "RX", channel));
                break;
            case P2P_AF_GO_DISC_REQ:
                CFGP2P_ACTION(("%s P2P Discoverability Request Frame,"
                               " channel=%d\n",
                               (tx) ? "TX" : "RX", channel));
                break;
            default:
                CFGP2P_ACTION(("%s Unknown P2P Action Frame,"
                               " channel=%d\n",
                               (tx) ? "TX" : "RX", channel));
        }
    } else if (wl_cfgp2p_is_gas_action(frame, frame_len)) {
        sd_act_frm = (wifi_p2psd_gas_pub_act_frame_t *)frame;
        switch (sd_act_frm->action) {
            case P2PSD_ACTION_ID_GAS_IREQ:
                CFGP2P_ACTION(("%s GAS Initial Request,"
                               " channel=%d\n",
                               (tx) ? "TX" : "RX", channel));
                break;
            case P2PSD_ACTION_ID_GAS_IRESP:
                CFGP2P_ACTION(("%s GAS Initial Response,"
                               " channel=%d\n",
                               (tx) ? "TX" : "RX", channel));
                break;
            case P2PSD_ACTION_ID_GAS_CREQ:
                CFGP2P_ACTION(("%s GAS Comback Request,"
                               " channel=%d\n",
                               (tx) ? "TX" : "RX", channel));
                break;
            case P2PSD_ACTION_ID_GAS_CRESP:
                CFGP2P_ACTION(("%s GAS Comback Response,"
                               " channel=%d\n",
                               (tx) ? "TX" : "RX", channel));
                break;
            default:
                CFGP2P_ACTION(("%s Unknown GAS Frame,"
                               " channel=%d\n",
                               (tx) ? "TX" : "RX", channel));
        }
    }
}

/*
 *  Initialize variables related to P2P
 *
 */
s32 wl_cfgp2p_init_priv(struct bcm_cfg80211 *cfg)
{
#ifdef WL_P2P_USE_RANDMAC
    struct ether_addr primary_mac;
#endif /* WL_P2P_USE_RANDMAC */
    cfg->p2p = MALLOCZ(cfg->osh, sizeof(struct p2p_info));
    if (cfg->p2p == NULL) {
        CFGP2P_ERR(("struct p2p_info allocation failed\n"));
        return -ENOMEM;
    }
#ifdef WL_P2P_USE_RANDMAC
    get_primary_mac(cfg, &primary_mac);
    wl_cfgp2p_generate_bss_mac(cfg, &primary_mac);
#endif /* WL_P2P_USE_RANDMAC */
    wl_to_p2p_bss_ndev(cfg, P2PAPI_BSSCFG_PRIMARY) = bcmcfg_to_prmry_ndev(cfg);
    wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_PRIMARY) = 0;
    wl_to_p2p_bss_ndev(cfg, P2PAPI_BSSCFG_DEVICE) = NULL;
    wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_DEVICE) = 0;
    wl_to_p2p_bss_ndev(cfg, P2PAPI_BSSCFG_CONNECTION1) = NULL;
    wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_CONNECTION1) = -1;
    wl_to_p2p_bss_ndev(cfg, P2PAPI_BSSCFG_CONNECTION2) = NULL;
    wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_CONNECTION2) = -1;
    return BCME_OK;
}
/*
 *  Deinitialize variables related to P2P
 *
 */
void wl_cfgp2p_deinit_priv(struct bcm_cfg80211 *cfg)
{
    CFGP2P_INFO(("In\n"));
    if (cfg->p2p) {
        MFREE(cfg->osh, cfg->p2p, sizeof(struct p2p_info));
        cfg->p2p = NULL;
    }
    cfg->p2p_supported = 0;
}
/*
 * Set P2P functions into firmware
 */
s32 wl_cfgp2p_set_firm_p2p(struct bcm_cfg80211 *cfg)
{
    struct net_device *ndev = bcmcfg_to_prmry_ndev(cfg);
    s32 ret = BCME_OK;
    s32 val = 0;
#ifdef WL_P2P_USE_RANDMAC
    struct ether_addr *p2p_dev_addr =
        wl_to_p2p_bss_macaddr(cfg, P2PAPI_BSSCFG_DEVICE);
#else
    struct ether_addr null_eth_addr = {{0, 0, 0, 0, 0, 0}};
    struct ether_addr *p2p_dev_addr = &null_eth_addr;
#endif // endif
#ifdef P2P_AP_CONCURRENT
    dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
    if (dhd->conf->war & P2P_AP_MAC_CONFLICT) {
        p2p_dev_addr = wl_to_p2p_bss_macaddr(cfg, P2PAPI_BSSCFG_DEVICE);
    }
#endif

    /* Do we have to check whether APSTA is enabled or not ? */
    ret = wldev_iovar_getint(ndev, "apsta", &val);
    if (ret < 0) {
        CFGP2P_ERR(("get apsta error %d\n", ret));
        return ret;
    }
    if (val == 0) {
        val = 1;
        ret = wldev_ioctl_set(ndev, WLC_DOWN, &val, sizeof(s32));
        if (ret < 0) {
            CFGP2P_ERR(("WLC_DOWN error %d\n", ret));
            return ret;
        }

        ret = wldev_iovar_setint(ndev, "apsta", val);
        if (ret < 0) {
            /* return error and fail the initialization */
            CFGP2P_ERR(("wl apsta %d set error. ret: %d\n", val, ret));
            return ret;
        }

        ret = wldev_ioctl_set(ndev, WLC_UP, &val, sizeof(s32));
        if (ret < 0) {
            CFGP2P_ERR(("WLC_UP error %d\n", ret));
            return ret;
        }
    }

    /* In case of COB type, firmware has default mac address
     * After Initializing firmware, we have to set current mac address to
     * firmware for P2P device address
     */
    ret = wldev_iovar_setbuf_bsscfg(ndev, "p2p_da_override", p2p_dev_addr,
                                    sizeof(*p2p_dev_addr), cfg->ioctl_buf,
                                    WLC_IOCTL_MAXLEN, 0, &cfg->ioctl_buf_sync);
    if (ret && ret != BCME_UNSUPPORTED) {
        CFGP2P_ERR(("failed to update device address ret %d\n", ret));
    }
    return ret;
}

int wl_cfg_multip2p_operational(struct bcm_cfg80211 *cfg)
{
    if (!cfg->p2p) {
        CFGP2P_DBG(("p2p not enabled! \n"));
        return false;
    }

    if ((wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_CONNECTION1) != -1) &&
        (wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_CONNECTION2) != -1)) {
        return true;
    } else {
        return false;
    }
}

/* Create a new P2P BSS.
 * Parameters
 * @mac      : MAC address of the BSS to create
 * @if_type  : interface type: WL_P2P_IF_GO or WL_P2P_IF_CLIENT
 * @chspec   : chspec to use if creating a GO BSS.
 * Returns 0 if success.
 */
s32 wl_cfgp2p_ifadd(struct bcm_cfg80211 *cfg, struct ether_addr *mac,
                    u8 if_type, chanspec_t chspec)
{
    wl_p2p_if_t ifreq;
    s32 err;
    struct net_device *ndev = bcmcfg_to_prmry_ndev(cfg);

    ifreq.type = if_type;
    ifreq.chspec = chspec;
    memcpy(ifreq.addr.octet, mac->octet, sizeof(ifreq.addr.octet));

    CFGP2P_ERR(("---cfg p2p_ifadd " MACDBG " %s %u\n",
                MAC2STRDBG(ifreq.addr.octet),
                (if_type == WL_P2P_IF_GO) ? "go" : "client",
                (chspec & WL_CHANSPEC_CHAN_MASK) >> WL_CHANSPEC_CHAN_SHIFT));

    err = wldev_iovar_setbuf(ndev, "p2p_ifadd", &ifreq, sizeof(ifreq),
                             cfg->ioctl_buf, WLC_IOCTL_MAXLEN,
                             &cfg->ioctl_buf_sync);
    if (unlikely(err < 0)) {
        CFGP2P_ERR(("'cfg p2p_ifadd' error %d\n", err));
        return err;
    }

    return err;
}

/* Disable a P2P BSS.
 * Parameters
 * @mac      : MAC address of the BSS to disable
 * Returns 0 if success.
 */
s32 wl_cfgp2p_ifdisable(struct bcm_cfg80211 *cfg, struct ether_addr *mac)
{
    s32 ret;
    struct net_device *netdev = bcmcfg_to_prmry_ndev(cfg);

    CFGP2P_INFO(("------ cfg p2p_ifdis " MACDBG " dev->ifindex:%d \n",
                 MAC2STRDBG(mac->octet), netdev->ifindex));
    ret = wldev_iovar_setbuf(netdev, "p2p_ifdis", mac, sizeof(*mac),
                             cfg->ioctl_buf, WLC_IOCTL_MAXLEN,
                             &cfg->ioctl_buf_sync);
    if (unlikely(ret < 0)) {
        CFGP2P_ERR(("'cfg p2p_ifdis' error %d\n", ret));
    }
    return ret;
}

/* Delete a P2P BSS.
 * Parameters
 * @mac      : MAC address of the BSS to delete
 * Returns 0 if success.
 */
s32 wl_cfgp2p_ifdel(struct bcm_cfg80211 *cfg, struct ether_addr *mac)
{
    s32 ret;
#ifdef WL_DISABLE_HE_P2P
    s32 bssidx = 0;
#endif /* WL_DISABLE_HE_P2P */
    struct net_device *netdev = bcmcfg_to_prmry_ndev(cfg);

    CFGP2P_ERR(("------ cfg p2p_ifdel " MACDBG " dev->ifindex:%d\n",
                MAC2STRDBG(mac->octet), netdev->ifindex));
    ret = wldev_iovar_setbuf(netdev, "p2p_ifdel", mac, sizeof(*mac),
                             cfg->ioctl_buf, WLC_IOCTL_MAXLEN,
                             &cfg->ioctl_buf_sync);
    if (unlikely(ret < 0)) {
        CFGP2P_ERR(("'cfg p2p_ifdel' error %d\n", ret));
    }
#ifdef WL_DISABLE_HE_P2P
    if ((bssidx = wl_get_bssidx_by_wdev(cfg, netdev->ieee80211_ptr)) < 0) {
        WL_ERR(("Find index failed\n"));
        ret = BCME_ERROR;
        return ret;
    }
    WL_DBG(("Enabling back HE for P2P\n"));
    wl_cfg80211_set_he_mode(netdev, cfg, bssidx, WL_IF_TYPE_P2P_DISC, TRUE);
    if (ret < 0) {
        WL_ERR(("failed to set he features, error=%d\n", ret));
    }
#endif /* WL_DISABLE_HE_P2P */

    return ret;
}

/* Change a P2P Role.
 * Parameters
 * @mac      : MAC address of the BSS to change a role
 * Returns 0 if success.
 */
s32 wl_cfgp2p_ifchange(struct bcm_cfg80211 *cfg, struct ether_addr *mac,
                       u8 if_type, chanspec_t chspec, s32 conn_idx)
{
    wl_p2p_if_t ifreq;
    s32 err;

    struct net_device *netdev = wl_to_p2p_bss_ndev(cfg, conn_idx);

    ifreq.type = if_type;
    ifreq.chspec = chspec;
    memcpy(ifreq.addr.octet, mac->octet, sizeof(ifreq.addr.octet));

    CFGP2P_INFO(("---cfg p2p_ifchange " MACDBG " %s %u"
                 " chanspec 0x%04x\n",
                 MAC2STRDBG(ifreq.addr.octet),
                 (if_type == WL_P2P_IF_GO) ? "go" : "client",
                 (chspec & WL_CHANSPEC_CHAN_MASK) >> WL_CHANSPEC_CHAN_SHIFT,
                 ifreq.chspec));

    err = wldev_iovar_setbuf(netdev, "p2p_ifupd", &ifreq, sizeof(ifreq),
                             cfg->ioctl_buf, WLC_IOCTL_MAXLEN,
                             &cfg->ioctl_buf_sync);
    if (unlikely(err < 0)) {
        CFGP2P_ERR(("'cfg p2p_ifupd' error %d\n", err));
    } else if (if_type == WL_P2P_IF_GO) {
        cfg->p2p->p2p_go_count++;
    }
    return err;
}

/* Get the index of a created P2P BSS.
 * Parameters
 * @mac      : MAC address of the created BSS
 * @index    : output: index of created BSS
 * Returns 0 if success.
 */
s32 wl_cfgp2p_ifidx(struct bcm_cfg80211 *cfg, struct ether_addr *mac,
                    s32 *index)
{
    s32 ret;
    u8 getbuf[64];
    struct net_device *dev = bcmcfg_to_prmry_ndev(cfg);

    CFGP2P_INFO(("---cfg p2p_if " MACDBG "\n", MAC2STRDBG(mac->octet)));

    ret = wldev_iovar_getbuf_bsscfg(
        dev, "p2p_if", mac, sizeof(*mac), getbuf, sizeof(getbuf),
        wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_PRIMARY), NULL);
    if (ret == 0) {
        memcpy(index, getbuf, sizeof(s32));
        CFGP2P_DBG(("---cfg p2p_if   ==> %d\n", *index));
    }
    return ret;
}

static s32 wl_cfgp2p_set_discovery(struct bcm_cfg80211 *cfg, s32 on)
{
    s32 ret = BCME_OK;
    struct net_device *ndev = bcmcfg_to_prmry_ndev(cfg);
    CFGP2P_DBG(("enter\n"));

    ret = wldev_iovar_setint(ndev, "p2p_disc", on);
    if (unlikely(ret < 0)) {
        CFGP2P_ERR(("p2p_disc %d error %d\n", on, ret));
    }

    return ret;
}

/* Set the WL driver's P2P mode.
 * Parameters
 * @mode      : is one of WL_P2P_DISC_ST_{SCAN,LISTEN,SEARCH}.
 * @channel   : the channel to listen
 * @listen_ms : the time (milli seconds) to wait
 * @bssidx    : bss index for BSSCFG
 * Returns 0 if success
 */

s32 wl_cfgp2p_set_p2p_mode(struct bcm_cfg80211 *cfg, u8 mode, u32 channel,
                           u16 listen_ms, int bssidx)
{
    wl_p2p_disc_st_t discovery_mode;
    s32 ret;
    struct net_device *dev;
    CFGP2P_DBG(("enter\n"));

    if (unlikely(bssidx == WL_INVALID)) {
        CFGP2P_ERR((" %d index out of range\n", bssidx));
        return -1;
    }

    dev = wl_cfgp2p_find_ndev(cfg, bssidx);
    if (unlikely(dev == NULL)) {
        CFGP2P_ERR(("bssidx %d is not assigned\n", bssidx));
        return BCME_NOTFOUND;
    }

#ifdef P2PLISTEN_AP_SAMECHN
    CFGP2P_DBG(("p2p0 listen channel %d  AP connection chan %d \n", channel,
                cfg->channel));
    if ((mode == WL_P2P_DISC_ST_LISTEN) && (cfg->channel == channel)) {
        struct net_device *primary_ndev = bcmcfg_to_prmry_ndev(cfg);

        if (cfg->p2p_resp_apchn_status) {
            CFGP2P_DBG(("p2p_resp_apchn_status already ON \n"));
            return BCME_OK;
        }

        if (wl_get_drv_status(cfg, CONNECTED, primary_ndev)) {
            ret = wl_cfg80211_set_p2p_resp_ap_chn(primary_ndev, 1);
            cfg->p2p_resp_apchn_status = true;
            CFGP2P_DBG(("p2p_resp_apchn_status ON \n"));
            return ret;
        }
    }
#endif /* P2PLISTEN_AP_SAMECHN */

    /* Put the WL driver into P2P Listen Mode to respond to P2P probe reqs */
    discovery_mode.state = mode;
    discovery_mode.chspec = wl_ch_host_to_driver(channel);
    discovery_mode.dwell = listen_ms;
    ret = wldev_iovar_setbuf_bsscfg(
        dev, "p2p_state", &discovery_mode, sizeof(discovery_mode),
        cfg->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &cfg->ioctl_buf_sync);

    return ret;
}

/* Get the index of the P2P Discovery BSS */
static s32 wl_cfgp2p_get_disc_idx(struct bcm_cfg80211 *cfg, s32 *index)
{
    s32 ret;
    struct net_device *dev = wl_to_p2p_bss_ndev(cfg, P2PAPI_BSSCFG_PRIMARY);

    ret = wldev_iovar_getint(dev, "p2p_dev", index);
    CFGP2P_INFO(("p2p_dev bsscfg_idx=%d ret=%d\n", *index, ret));

    if (unlikely(ret < 0)) {
        CFGP2P_ERR(("'p2p_dev' error %d\n", ret));
        return ret;
    }
    return ret;
}

int wl_cfgp2p_get_conn_idx(struct bcm_cfg80211 *cfg)
{
    int i;
    s32 connected_cnt;
    dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
    if (!dhd) {
        return (-ENODEV);
    }
    for (i = P2PAPI_BSSCFG_CONNECTION1; i < P2PAPI_BSSCFG_MAX; i++) {
        if (wl_to_p2p_bss_bssidx(cfg, i) == -1) {
            if (i == P2PAPI_BSSCFG_CONNECTION2) {
                if (!(dhd->op_mode & DHD_FLAG_MP2P_MODE)) {
                    CFGP2P_ERR(("Multi p2p not supported"));
                    return BCME_ERROR;
                }
                if ((connected_cnt = wl_get_drv_status_all(cfg, CONNECTED)) >
                    1) {
                    CFGP2P_ERR(("Failed to create second p2p interface"
                                "Already one connection exists"));
                    return BCME_ERROR;
                }
            }
            return i;
        }
    }
    return BCME_ERROR;
}

s32 wl_cfgp2p_init_discovery(struct bcm_cfg80211 *cfg)
{
    s32 bssidx = 0;
    s32 ret = BCME_OK;
    struct net_device *ndev = bcmcfg_to_prmry_ndev(cfg);

    BCM_REFERENCE(ndev);
    CFGP2P_DBG(("enter\n"));

    if (wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_DEVICE) > 0) {
        CFGP2P_ERR(("do nothing, already initialized\n"));
        goto exit;
    }

    ret = wl_cfgp2p_set_discovery(cfg, 1);
    if (ret < 0) {
        CFGP2P_ERR(("set discover error\n"));
        goto exit;
    }
    /* Enable P2P Discovery in the WL Driver */
    ret = wl_cfgp2p_get_disc_idx(cfg, &bssidx);
    if (ret < 0) {
        goto exit;
    }

    /* In case of CFG80211 case, check if p2p_discovery interface has allocated
     * p2p_wdev */
    if (!cfg->p2p_wdev) {
        CFGP2P_ERR(("p2p_wdev is NULL.\n"));
        ret = -ENODEV;
        goto exit;
    }

    /* Once p2p also starts using interface_create iovar, the ifidx may change.
     * so that time, the ifidx returned in WLC_E_IF should be used for
     * populating the netinfo
     */
    ret = wl_alloc_netinfo(cfg, NULL, cfg->p2p_wdev, WL_IF_TYPE_STA, 0, bssidx,
                           0);
    if (unlikely(ret)) {
        goto exit;
    }
    wl_to_p2p_bss_ndev(cfg, P2PAPI_BSSCFG_DEVICE) =
        wl_to_p2p_bss_ndev(cfg, P2PAPI_BSSCFG_PRIMARY);
    wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_DEVICE) = bssidx;

    /* Set the initial discovery state to SCAN */
    ret =
        wl_cfgp2p_set_p2p_mode(cfg, WL_P2P_DISC_ST_SCAN, 0, 0,
                               wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_DEVICE));
    if (unlikely(ret != 0)) {
        CFGP2P_ERR(("unable to set WL_P2P_DISC_ST_SCAN\n"));
        wl_cfgp2p_set_discovery(cfg, 0);
        wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_DEVICE) = 0;
        wl_to_p2p_bss_ndev(cfg, P2PAPI_BSSCFG_DEVICE) = NULL;
        ret = 0;
        goto exit;
    }

    /* Clear our saved WPS and P2P IEs for the discovery BSS */
    wl_cfg80211_clear_p2p_disc_ies(cfg);
exit:
    if (ret) {
        wl_flush_fw_log_buffer(ndev, FW_LOGSET_MASK_ALL);
    }
    return ret;
}

/* Deinitialize P2P Discovery
 * Parameters
 * @cfg        : wl_private data
 * Returns 0 if succes
 */
static s32 wl_cfgp2p_deinit_discovery(struct bcm_cfg80211 *cfg)
{
    s32 ret = BCME_OK;
    s32 bssidx;

    CFGP2P_DBG(("enter\n"));
    bssidx = wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_DEVICE);
    if (bssidx <= 0) {
        CFGP2P_ERR(("do nothing, not initialized\n"));
        return -1;
    }

    /* Clear our saved WPS and P2P IEs for the discovery BSS */
    wl_cfg80211_clear_p2p_disc_ies(cfg);

    /* Set the discovery state to SCAN */
    wl_cfgp2p_set_p2p_mode(cfg, WL_P2P_DISC_ST_SCAN, 0, 0, bssidx);
    /* Disable P2P discovery in the WL driver (deletes the discovery BSSCFG) */
    ret = wl_cfgp2p_set_discovery(cfg, 0);

    /* Remove the p2p disc entry in the netinfo */
    wl_dealloc_netinfo_by_wdev(cfg, cfg->p2p_wdev);

    wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_DEVICE) = WL_INVALID;
    wl_to_p2p_bss_ndev(cfg, P2PAPI_BSSCFG_DEVICE) = NULL;

    return ret;
}
/* Enable P2P Discovery
 * Parameters
 * @cfg	: wl_private data
 * @ie  : probe request ie (WPS IE + P2P IE)
 * @ie_len   : probe request ie length
 * Returns 0 if success.
 */
s32 wl_cfgp2p_enable_discovery(struct bcm_cfg80211 *cfg, struct net_device *dev,
                               const u8 *ie, u32 ie_len)
{
    s32 ret = BCME_OK;
    s32 bssidx;
    bcm_struct_cfgdev *cfgdev;

    CFGP2P_DBG(("enter\n"));
    mutex_lock(&cfg->if_sync);
#ifdef WL_IFACE_MGMT
    if ((ret = wl_cfg80211_handle_if_role_conflict(cfg, WL_IF_TYPE_P2P_DISC)) !=
        BCME_OK) {
        WL_ERR(("secondary iface is active, p2p enable discovery is not "
                "supported\n"));
        goto exit;
    }
#endif /* WL_IFACE_MGMT */

    if (wl_get_p2p_status(cfg, DISCOVERY_ON)) {
        CFGP2P_DBG(
            (" DISCOVERY is already initialized, we have nothing to do\n"));
        goto set_ie;
    }

    ret = wl_cfgp2p_init_discovery(cfg);
    if (unlikely(ret < 0)) {
        CFGP2P_ERR((" init discovery error %d\n", ret));
        goto exit;
    }

    wl_set_p2p_status(cfg, DISCOVERY_ON);
    /* Set wsec to any non-zero value in the discovery bsscfg to ensure our
     * P2P probe responses have the privacy bit set in the 802.11 WPA IE.
     * Some peer devices may not initiate WPS with us if this bit is not set.
     */
    ret = wldev_iovar_setint_bsscfg(
        wl_to_p2p_bss_ndev(cfg, P2PAPI_BSSCFG_DEVICE), "wsec", AES_ENABLED,
        wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_DEVICE));
    if (unlikely(ret < 0)) {
        CFGP2P_ERR((" wsec error %d\n", ret));
    }
set_ie:
    if (ie_len) {
        if (bcmcfg_to_prmry_ndev(cfg) == dev) {
            bssidx = wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_DEVICE);
        } else if ((bssidx = wl_get_bssidx_by_wdev(cfg, cfg->p2p_wdev)) < 0) {
            WL_ERR(("Find p2p index from wdev(%p) failed\n", cfg->p2p_wdev));
            ret = BCME_ERROR;
            goto exit;
        }
#if defined(WL_CFG80211_P2P_DEV_IF)
        /* For 3.8+ kernels, pass p2p discovery wdev */
        cfgdev = cfg->p2p_wdev;
#else
        /* Prior to 3.8 kernel, there is no netless p2p, so pass p2p0 ndev */
        cfgdev = ndev_to_cfgdev(dev);
#endif /* WL_CFG80211_P2P_DEV_IF */
        ret = wl_cfg80211_set_mgmt_vndr_ies(cfg, cfgdev, bssidx,
                                            VNDR_IE_PRBREQ_FLAG, ie, ie_len);
        if (unlikely(ret < 0)) {
            CFGP2P_ERR(("set probreq ie occurs error %d\n", ret));
            goto exit;
        }
    }
exit:
    if (ret) {
        wl_flush_fw_log_buffer(dev, FW_LOGSET_MASK_ALL);
    }
    mutex_unlock(&cfg->if_sync);
    return ret;
}

/* Disable P2P Discovery
 * Parameters
 * @cfg       : wl_private_data
 * Returns 0 if success.
 */
s32 wl_cfgp2p_disable_discovery(struct bcm_cfg80211 *cfg)
{
    s32 ret = BCME_OK;
    s32 bssidx;

    CFGP2P_DBG((" enter\n"));
    wl_clr_p2p_status(cfg, DISCOVERY_ON);

    if (!cfg->p2p) { // terence 20130113: Fix for p2p NULL pointer
        ret = BCME_ERROR;
        CFGP2P_ERR(("wl->p2p is NULL\n"));
        goto exit;
    }

#ifdef DHD_IFDEBUG
    WL_ERR(("%s: bssidx: %d\n", __FUNCTION__,
            (cfg)->p2p->bss[P2PAPI_BSSCFG_DEVICE].bssidx));
#endif // endif
    bssidx = wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_DEVICE);
    if (bssidx <= 0) {
        CFGP2P_ERR((" do nothing, not initialized\n"));
        return 0;
    }

    ret = wl_cfgp2p_set_p2p_mode(cfg, WL_P2P_DISC_ST_SCAN, 0, 0, bssidx);
    if (unlikely(ret < 0)) {
        CFGP2P_ERR(("unable to set WL_P2P_DISC_ST_SCAN\n"));
    }
    /* Do a scan abort to stop the driver's scan engine in case it is still
     * waiting out an action frame tx dwell time.
     */
#ifdef NOT_YET
    if (wl_get_p2p_status(cfg, SCANNING)) {
        p2pwlu_scan_abort(hdl, FALSE);
    }
#endif // endif
    wl_clr_p2p_status(cfg, DISCOVERY_ON);
    ret = wl_cfgp2p_deinit_discovery(cfg);

exit:
    return ret;
}

/* Scan parameters */
#define P2PAPI_SCAN_NPROBES 1
#define P2PAPI_SCAN_DWELL_TIME_MS 80
#define P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS 40
#define P2PAPI_SCAN_HOME_TIME_MS 60
#define P2PAPI_SCAN_NPROBS_TIME_MS 30
#define P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS 100
s32 wl_cfgp2p_escan(struct bcm_cfg80211 *cfg, struct net_device *dev,
                    u16 active_scan, u32 num_chans, u16 *channels,
                    s32 search_state, u16 action, u32 bssidx,
                    struct ether_addr *tx_dst_addr,
                    p2p_scan_purpose_t p2p_scan_purpose)
{
    s32 ret = BCME_OK;
    s32 memsize;
    s32 eparams_size;
    u32 i;
    s8 *memblk;
    wl_p2p_scan_t *p2p_params;
    wl_escan_params_t *eparams;
    wl_escan_params_v2_t *eparams_v2;
    wlc_ssid_t ssid;
    u32 sync_id = 0;
    s32 nprobes = 0;
    s32 active_time = 0;
    const struct ether_addr *mac_addr = NULL;
    u32 scan_type = 0;
    struct net_device *pri_dev = NULL;

    pri_dev = wl_to_p2p_bss_ndev(cfg, P2PAPI_BSSCFG_PRIMARY);
    /* Allocate scan params which need space for 3 channels and 0 ssids */
    if (cfg->scan_params_v2) {
        eparams_size = (WL_SCAN_PARAMS_V2_FIXED_SIZE +
                        OFFSETOF(wl_escan_params_v2_t, params)) +
                       num_chans * sizeof(eparams->params.channel_list[0]);
    } else {
        eparams_size =
            (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_escan_params_t, params)) +
            num_chans * sizeof(eparams->params.channel_list[0]);
    }

    memsize = sizeof(wl_p2p_scan_t) + eparams_size;
    memblk = scanparambuf;
    if (memsize > sizeof(scanparambuf)) {
        CFGP2P_ERR((" scanpar buf too small (%u > %zu)\n", memsize,
                    sizeof(scanparambuf)));
        return -1;
    }
    bzero(memblk, memsize);
    bzero(cfg->ioctl_buf, WLC_IOCTL_MAXLEN);
    if (search_state == WL_P2P_DISC_ST_SEARCH) {
        /*
         * If we in SEARCH STATE, we don't need to set SSID explictly
         * because dongle use P2P WILDCARD internally by default
         */
        wl_cfgp2p_set_p2p_mode(cfg, WL_P2P_DISC_ST_SEARCH, 0, 0, bssidx);
        /* use null ssid */
        ssid.SSID_len = 0;
        bzero(&ssid.SSID, sizeof(ssid.SSID));
    } else if (search_state == WL_P2P_DISC_ST_SCAN) {
        /* SCAN STATE 802.11 SCAN
         * WFD Supplicant has p2p_find command with (type=progressive, type=
         * full) So if P2P_find command with type=progressive, we have to set
         * ssid to P2P WILDCARD because we just do broadcast scan unless setting
         * SSID
         */
        wl_cfgp2p_set_p2p_mode(cfg, WL_P2P_DISC_ST_SCAN, 0, 0, bssidx);
        /* use wild card ssid */
        ssid.SSID_len = WL_P2P_WILDCARD_SSID_LEN;
        bzero(&ssid.SSID, sizeof(ssid.SSID));
        memcpy(&ssid.SSID, WL_P2P_WILDCARD_SSID, WL_P2P_WILDCARD_SSID_LEN);
    } else {
        CFGP2P_ERR((" invalid search state %d\n", search_state));
        return -1;
    }

    /* Fill in the P2P scan structure at the start of the iovar param block */
    p2p_params = (wl_p2p_scan_t *)memblk;
    p2p_params->type = 'E';

    if (!active_scan) {
        scan_type = WL_SCANFLAGS_PASSIVE;
    }

    if (tx_dst_addr == NULL) {
        mac_addr = &ether_bcast;
    } else {
        mac_addr = tx_dst_addr;
    }

    switch (p2p_scan_purpose) {
        case P2P_SCAN_SOCIAL_CHANNEL:
            active_time = P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS;
            break;
        case P2P_SCAN_AFX_PEER_NORMAL:
        case P2P_SCAN_AFX_PEER_REDUCED:
            active_time = P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS;
            break;
        case P2P_SCAN_CONNECT_TRY:
            active_time = WL_SCAN_CONNECT_DWELL_TIME_MS;
            break;
        default:
            active_time = wl_get_drv_status_all(cfg, CONNECTED)
                              ? -1
                              : P2PAPI_SCAN_DWELL_TIME_MS;
            break;
    }

    if (p2p_scan_purpose == P2P_SCAN_CONNECT_TRY) {
        nprobes = active_time / WL_SCAN_JOIN_PROBE_INTERVAL_MS;
    } else {
        nprobes = active_time / P2PAPI_SCAN_NPROBS_TIME_MS;
    }

    if (nprobes <= 0) {
        nprobes = 1;
    }

    wl_escan_set_sync_id(sync_id, cfg);
    /* Fill in the Scan structure that follows the P2P scan structure */
    if (cfg->scan_params_v2) {
        eparams_v2 = (wl_escan_params_v2_t *)(p2p_params + 1);
        eparams_v2->version = htod16(ESCAN_REQ_VERSION_V2);
        eparams_v2->action = htod16(action);
        eparams_v2->params.version = htod16(WL_SCAN_PARAMS_VERSION_V2);
        eparams_v2->params.length = htod16(sizeof(wl_scan_params_v2_t));
        eparams_v2->params.bss_type = DOT11_BSSTYPE_ANY;
        eparams_v2->params.scan_type = htod32(scan_type);
        (void)memcpy_s(&eparams_v2->params.bssid, ETHER_ADDR_LEN, mac_addr,
                       ETHER_ADDR_LEN);
        eparams_v2->params.home_time = htod32(P2PAPI_SCAN_HOME_TIME_MS);
        eparams_v2->params.active_time = htod32(active_time);
        eparams_v2->params.nprobes = htod32(nprobes);
        eparams_v2->params.passive_time = htod32(-1);
        eparams_v2->sync_id = sync_id;
        for (i = 0; i < num_chans; i++) {
            eparams_v2->params.channel_list[i] =
                wl_ch_host_to_driver(channels[i]);
        }
        eparams_v2->params.channel_num =
            htod32((0 << WL_SCAN_PARAMS_NSSID_SHIFT) |
                   (num_chans & WL_SCAN_PARAMS_COUNT_MASK));
        if (ssid.SSID_len) {
            (void)memcpy_s(&eparams_v2->params.ssid, sizeof(wlc_ssid_t), &ssid,
                           sizeof(wlc_ssid_t));
        }
        sync_id = eparams_v2->sync_id;
    } else {
        eparams = (wl_escan_params_t *)(p2p_params + 1);
        eparams->version = htod32(ESCAN_REQ_VERSION);
        eparams->action = htod16(action);
        eparams->params.bss_type = DOT11_BSSTYPE_ANY;
        eparams->params.scan_type = htod32(scan_type);
        (void)memcpy_s(&eparams->params.bssid, ETHER_ADDR_LEN, mac_addr,
                       ETHER_ADDR_LEN);
        eparams->params.home_time = htod32(P2PAPI_SCAN_HOME_TIME_MS);
        eparams->params.active_time = htod32(active_time);
        eparams->params.nprobes = htod32(nprobes);
        eparams->params.passive_time = htod32(-1);
        eparams->sync_id = sync_id;
        for (i = 0; i < num_chans; i++) {
            eparams->params.channel_list[i] = wl_ch_host_to_driver(channels[i]);
        }
        eparams->params.channel_num =
            htod32((0 << WL_SCAN_PARAMS_NSSID_SHIFT) |
                   (num_chans & WL_SCAN_PARAMS_COUNT_MASK));
        if (ssid.SSID_len) {
            (void)memcpy_s(&eparams->params.ssid, sizeof(wlc_ssid_t), &ssid,
                           sizeof(wlc_ssid_t));
        }
        sync_id = eparams->sync_id;
    }

    wl_escan_set_type(cfg, WL_SCANTYPE_P2P);

    CFGP2P_DBG(("nprobes:%d active_time:%d\n", nprobes, active_time));
    CFGP2P_DBG(("SCAN CHANNELS : "));
    CFGP2P_DBG(("%d", channels[0]));
    for (i = 1; i < num_chans; i++) {
        CFGP2P_DBG((",%d", channels[i]));
    }
    CFGP2P_DBG(("\n"));

    WL_MSG(dev->name, "P2P_SEARCH sync ID: %d, bssidx: %d\n", sync_id, bssidx);
    ret = wldev_iovar_setbuf_bsscfg(pri_dev, "p2p_scan", memblk, memsize,
                                    cfg->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx,
                                    &cfg->ioctl_buf_sync);
    if (ret == BCME_OK) {
        wl_set_p2p_status(cfg, SCANNING);
    }
    return ret;
}

/* search function to reach at common channel to send action frame
 * Parameters
 * @cfg       : wl_private data
 * @ndev     : net device for bssidx
 * @bssidx   : bssidx for BSS
 * Returns 0 if success.
 */
s32 wl_cfgp2p_act_frm_search(struct bcm_cfg80211 *cfg, struct net_device *ndev,
                             s32 bssidx, s32 channel,
                             struct ether_addr *tx_dst_addr)
{
    s32 ret = 0;
    u32 chan_cnt = 0;
    u16 *default_chan_list = NULL;
    p2p_scan_purpose_t p2p_scan_purpose = P2P_SCAN_AFX_PEER_NORMAL;
    if (!p2p_is_on(cfg) || ndev == NULL || bssidx == WL_INVALID) {
        return -EINVAL;
    }
    WL_TRACE_HW4((" Enter\n"));
    if (bssidx == wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_PRIMARY)) {
        bssidx = wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_DEVICE);
    }
    if (channel) {
        chan_cnt = AF_PEER_SEARCH_CNT;
    } else {
        chan_cnt = SOCIAL_CHAN_CNT;
    }

    if (cfg->afx_hdl->pending_tx_act_frm && cfg->afx_hdl->is_active) {
        wl_action_frame_t *action_frame;
        action_frame = &(cfg->afx_hdl->pending_tx_act_frm->action_frame);
        if (wl_cfgp2p_is_p2p_gas_action(action_frame->data,
                                        action_frame->len)) {
            chan_cnt = 1;
            p2p_scan_purpose = P2P_SCAN_AFX_PEER_REDUCED;
        }
    }

    default_chan_list =
        (u16 *)MALLOCZ(cfg->osh, chan_cnt * sizeof(*default_chan_list));
    if (default_chan_list == NULL) {
        CFGP2P_ERR(("channel list allocation failed \n"));
        ret = -ENOMEM;
        goto exit;
    }
    if (channel) {
        u32 i;
        /* insert same channel to the chan_list */
        for (i = 0; i < chan_cnt; i++) {
            default_chan_list[i] = channel;
        }
    } else {
        default_chan_list[0] = SOCIAL_CHAN_1;
        default_chan_list[1] = SOCIAL_CHAN_2;
        default_chan_list[0x2] = SOCIAL_CHAN_3;
    }
    ret = wl_cfgp2p_escan(cfg, ndev, true, chan_cnt, default_chan_list,
                          WL_P2P_DISC_ST_SEARCH, WL_SCAN_ACTION_START, bssidx,
                          NULL, p2p_scan_purpose);
    MFREE(cfg->osh, default_chan_list, chan_cnt * sizeof(*default_chan_list));
exit:
    return ret;
}

/* Check whether pointed-to IE looks like WPA. */
#define wl_cfgp2p_is_wpa_ie(ie, tlvs, len)                                     \
    wl_cfgp2p_has_ie(ie, tlvs, len, (const uint8 *)WPS_OUI, WPS_OUI_LEN,       \
                     WPA_OUI_TYPE)
/* Check whether pointed-to IE looks like WPS. */
#define wl_cfgp2p_is_wps_ie(ie, tlvs, len)                                     \
    wl_cfgp2p_has_ie(ie, tlvs, len, (const uint8 *)WPS_OUI, WPS_OUI_LEN,       \
                     WPS_OUI_TYPE)
/* Check whether the given IE looks like WFA P2P IE. */
#define wl_cfgp2p_is_p2p_ie(ie, tlvs, len)                                     \
    wl_cfgp2p_has_ie(ie, tlvs, len, (const uint8 *)WFA_OUI, WFA_OUI_LEN,       \
                     WFA_OUI_TYPE_P2P)
/* Check whether the given IE looks like WFA WFDisplay IE. */
#ifndef WFA_OUI_TYPE_WFD
#define WFA_OUI_TYPE_WFD 0x0a /* WiFi Display OUI TYPE */
#endif                        // endif
#define wl_cfgp2p_is_wfd_ie(ie, tlvs, len)                                     \
    wl_cfgp2p_has_ie(ie, tlvs, len, (const uint8 *)WFA_OUI, WFA_OUI_LEN,       \
                     WFA_OUI_TYPE_WFD)

/* Is any of the tlvs the expected entry? If
 * not update the tlvs buffer pointer/length.
 */
static bool wl_cfgp2p_has_ie(const bcm_tlv_t *ie, const u8 **tlvs,
                             u32 *tlvs_len, const u8 *oui, u32 oui_len, u8 type)
{
    /* If the contents match the OUI and the type */
    if (ie->len >= oui_len + 1 && !bcmp(ie->data, oui, oui_len) &&
        type == ie->data[oui_len]) {
        return TRUE;
    }

    /* point to the next ie */
    if (tlvs != NULL) {
        bcm_tlv_buffer_advance_past(ie, tlvs, tlvs_len);
    }

    return FALSE;
}

const wpa_ie_fixed_t *wl_cfgp2p_find_wpaie(const u8 *parse, u32 len)
{
    const bcm_tlv_t *ie;

    while ((ie = bcm_parse_tlvs(parse, len, DOT11_MNG_VS_ID))) {
        if (wl_cfgp2p_is_wpa_ie(ie, &parse, &len)) {
            return (const wpa_ie_fixed_t *)ie;
        }
    }
    return NULL;
}

const wpa_ie_fixed_t *wl_cfgp2p_find_wpsie(const u8 *parse, u32 len)
{
    const bcm_tlv_t *ie;

    while ((ie = bcm_parse_tlvs(parse, len, DOT11_MNG_VS_ID))) {
        if (wl_cfgp2p_is_wps_ie(ie, &parse, &len)) {
            return (const wpa_ie_fixed_t *)ie;
        }
    }
    return NULL;
}

wifi_p2p_ie_t *wl_cfgp2p_find_p2pie(const u8 *parse, u32 len)
{
    bcm_tlv_t *ie;

    while ((ie = bcm_parse_tlvs(parse, len, DOT11_MNG_VS_ID))) {
        if (wl_cfgp2p_is_p2p_ie(ie, &parse, &len)) {
            return (wifi_p2p_ie_t *)ie;
        }
    }
    return NULL;
}

const wifi_wfd_ie_t *wl_cfgp2p_find_wfdie(const u8 *parse, u32 len)
{
    const bcm_tlv_t *ie;

    while ((ie = bcm_parse_tlvs(parse, len, DOT11_MNG_VS_ID))) {
        if (wl_cfgp2p_is_wfd_ie(ie, &parse, &len)) {
            return (const wifi_wfd_ie_t *)ie;
        }
    }
    return NULL;
}

u32 wl_cfgp2p_vndr_ie(struct bcm_cfg80211 *cfg, u8 *iebuf, s32 pktflag, s8 *oui,
                      s32 ie_id, const s8 *data, s32 datalen,
                      const s8 *add_del_cmd)
{
    vndr_ie_setbuf_t hdr; /* aligned temporary vndr_ie buffer header */
    s32 iecount;
    u32 data_offset;

    /* Validate the pktflag parameter */
    if ((pktflag &
         ~(VNDR_IE_BEACON_FLAG | VNDR_IE_PRBRSP_FLAG | VNDR_IE_ASSOCRSP_FLAG |
           VNDR_IE_AUTHRSP_FLAG | VNDR_IE_PRBREQ_FLAG | VNDR_IE_ASSOCREQ_FLAG |
           VNDR_IE_DISASSOC_FLAG))) {
        CFGP2P_ERR(("p2pwl_vndr_ie: Invalid packet flag 0x%x\n", pktflag));
        return -1;
    }

    /* Copy the vndr_ie SET command ("add"/"del") to the buffer */
    strlcpy(hdr.cmd, add_del_cmd, sizeof(hdr.cmd));

    /* Set the IE count - the buffer contains only 1 IE */
    iecount = htod32(1);
    memcpy((void *)&hdr.vndr_ie_buffer.iecount, &iecount, sizeof(s32));

    /* For vendor ID DOT11_MNG_ID_EXT_ID, need to set pkt flag to
     * VNDR_IE_CUSTOM_FLAG */
    if (ie_id == DOT11_MNG_ID_EXT_ID) {
        pktflag = pktflag | VNDR_IE_CUSTOM_FLAG;
    }

    /* Copy packet flags that indicate which packets will contain this IE */
    pktflag = htod32(pktflag);
    memcpy((void *)&hdr.vndr_ie_buffer.vndr_ie_list[0].pktflag, &pktflag,
           sizeof(u32));

    /* Add the IE ID to the buffer */
    hdr.vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.id = ie_id;

    /* Add the IE length to the buffer */
    hdr.vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.len =
        (uint8)VNDR_IE_MIN_LEN + datalen;

    /* Add the IE OUI to the buffer */
    hdr.vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.oui[0] = oui[0];
    hdr.vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.oui[1] = oui[1];
    hdr.vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.oui[0x2] = oui[0x2];

    /* Copy the aligned temporary vndr_ie buffer header to the IE buffer */
    memcpy(iebuf, &hdr, sizeof(hdr) - 1);

    /* Copy the IE data to the IE buffer */
    data_offset =
        (u8 *)&hdr.vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.data[0] -
        (u8 *)&hdr;
    memcpy(iebuf + data_offset, data, datalen);
    return data_offset + datalen;
}

struct net_device *wl_cfgp2p_find_ndev(struct bcm_cfg80211 *cfg, s32 bssidx)
{
    u32 i;
    struct net_device *ndev = NULL;
    if (bssidx < 0) {
        CFGP2P_ERR((" bsscfg idx is invalid\n"));
        goto exit;
    }

    for (i = 0; i < P2PAPI_BSSCFG_MAX; i++) {
        if (bssidx == wl_to_p2p_bss_bssidx(cfg, i)) {
            ndev = wl_to_p2p_bss_ndev(cfg, i);
            break;
        }
    }

exit:
    return ndev;
}
/*
 * Search the driver array idx based on bssidx argument
 * Parameters: Note that this idx is applicable only
 * for primary and P2P interfaces. The virtual AP/STA is not
 * covered here.
 * @cfg     : wl_private data
 * @bssidx : bssidx which indicate bsscfg->idx of firmware.
 * @type   : output arg to store array idx of p2p->bss.
 * Returns error
 */

s32 wl_cfgp2p_find_type(struct bcm_cfg80211 *cfg, s32 bssidx, s32 *type)
{
    u32 i;
    if (bssidx < 0 || type == NULL) {
        CFGP2P_ERR((" argument is invalid\n"));
        goto exit;
    }
    if (!cfg->p2p) {
        CFGP2P_ERR(("p2p if does not exist\n"));
        goto exit;
    }
    for (i = 0; i < P2PAPI_BSSCFG_MAX; i++) {
        if (bssidx == wl_to_p2p_bss_bssidx(cfg, i)) {
            *type = i;
            return BCME_OK;
        }
    }

exit:
    return BCME_BADARG;
}

/*
 * Callback function for WLC_E_P2P_DISC_LISTEN_COMPLETE
 */
s32 wl_cfgp2p_listen_complete(struct bcm_cfg80211 *cfg,
                              bcm_struct_cfgdev *cfgdev,
                              const wl_event_msg_t *e, void *data)
{
    s32 ret = BCME_OK;
    struct net_device *ndev = NULL;

    if (!cfg || !cfg->p2p || !cfgdev) {
        return BCME_ERROR;
    }

    CFGP2P_DBG((" Enter\n"));
#ifdef DHD_IFDEBUG
    PRINT_WDEV_INFO(cfgdev);
#endif /* DHD_IFDEBUG */

    ndev = cfgdev_to_wlc_ndev(cfgdev, cfg);

#ifdef P2P_LISTEN_OFFLOADING
    if (wl_get_p2p_status(cfg, DISC_IN_PROGRESS)) {
        wl_clr_p2p_status(cfg, DISC_IN_PROGRESS);
        CFGP2P_ERR(("DISC_IN_PROGRESS cleared\n"));
        if (ndev && (ndev->ieee80211_ptr != NULL)) {
#if defined(WL_CFG80211_P2P_DEV_IF)
            if (cfgdev && ((struct wireless_dev *)cfgdev)->wiphy) {
                cfg80211_remain_on_channel_expired(
                    cfgdev, cfg->last_roc_id, &cfg->remain_on_chan, GFP_KERNEL);
            } else {
                CFGP2P_ERR(("Invalid cfgdev. Dropping the"
                            "remain_on_channel_expired event.\n"));
            }
#else
            cfg80211_remain_on_channel_expired(
                cfgdev, cfg->last_roc_id, &cfg->remain_on_chan,
                cfg->remain_on_chan_type, GFP_KERNEL);
#endif /* WL_CFG80211_P2P_DEV_IF */

#ifdef CONFIG_AP6XXX_WIFI6_HDF
            ret = HdfWifiEventCancelRemainOnChannel(
                get_hdf_netdev(g_event_ifidx), cfg->remain_on_chan.center_freq);
            WL_ERR(("HdfWifiEventCancelRemainOnChannel ret=%d\n", ret));
#endif
        }
    }
#endif /* P2P_LISTEN_OFFLOADING */

    if (wl_get_p2p_status(cfg, LISTEN_EXPIRED) == 0) {
        wl_set_p2p_status(cfg, LISTEN_EXPIRED);
        if (timer_pending(&cfg->p2p->listen_timer)) {
            del_timer_sync(&cfg->p2p->listen_timer);
        }

        if (cfg->afx_hdl->is_listen == TRUE &&
            wl_get_drv_status_all(cfg, FINDING_COMMON_CHANNEL)) {
            WL_DBG(("Listen DONE for action frame\n"));
            complete(&cfg->act_frm_scan);
        }
#ifdef WL_CFG80211_SYNC_GON
        else if (wl_get_drv_status_all(cfg, WAITING_NEXT_ACT_FRM_LISTEN)) {
            wl_clr_drv_status(cfg, WAITING_NEXT_ACT_FRM_LISTEN, ndev);
            WL_DBG(("Listen DONE and wake up wait_next_af !!(%d)\n",
                    jiffies_to_msecs(jiffies - cfg->af_tx_sent_jiffies)));

            if (wl_get_drv_status_all(cfg, WAITING_NEXT_ACT_FRM)) {
                wl_clr_drv_status(cfg, WAITING_NEXT_ACT_FRM, ndev);
            }

            complete(&cfg->wait_next_af);
        }
#endif /* WL_CFG80211_SYNC_GON */

#ifndef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST
        if (wl_get_drv_status_all(cfg, REMAINING_ON_CHANNEL))
#else
        if (wl_get_drv_status_all(cfg, REMAINING_ON_CHANNEL) ||
            wl_get_drv_status_all(cfg, FAKE_REMAINING_ON_CHANNEL))
#endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */
        {
            WL_DBG(("Listen DONE for remain on channel expired\n"));
            wl_clr_drv_status(cfg, REMAINING_ON_CHANNEL, ndev);
#ifdef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST
            wl_clr_drv_status(cfg, FAKE_REMAINING_ON_CHANNEL, ndev);
#endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */
            if (ndev && (ndev->ieee80211_ptr != NULL)) {
#if defined(WL_CFG80211_P2P_DEV_IF)
                if (cfgdev && ((struct wireless_dev *)cfgdev)->wiphy &&
                    bcmcfg_to_p2p_wdev(cfg)) {
                    /*
                     * To prevent kernel panic,
                     * if cfgdev->wiphy may be invalid, adding explicit check
                     */
                    cfg80211_remain_on_channel_expired(
                        bcmcfg_to_p2p_wdev(cfg), cfg->last_roc_id,
                        &cfg->remain_on_chan, GFP_KERNEL);
#ifdef CONFIG_AP6XXX_WIFI6_HDF
                    ret = HdfWifiEventCancelRemainOnChannel(
                        get_hdf_netdev(g_event_ifidx),
                        cfg->remain_on_chan.center_freq);
                    WL_ERR(("HdfWifiEventCancelRemainOnChannel ret=%d\n", ret));
#endif
                } else {
                    CFGP2P_ERR(("Invalid cfgdev. Dropping the"
                                "remain_on_channel_expired event.\n"));
                }
#else
                if (cfgdev && ((struct wireless_dev *)cfgdev)->wiphy) {
                    cfg80211_remain_on_channel_expired(
                        cfgdev, cfg->last_roc_id, &cfg->remain_on_chan,
                        cfg->remain_on_chan_type, GFP_KERNEL);
                }

#ifdef CONFIG_AP6XXX_WIFI6_HDF
                ret = HdfWifiEventCancelRemainOnChannel(
                    get_hdf_netdev(g_event_ifidx),
                    cfg->remain_on_chan.center_freq);
                WL_ERR(("HdfWifiEventCancelRemainOnChannel ret=%d\n", ret));
#endif
#endif /* WL_CFG80211_P2P_DEV_IF */
            }
        }
        if (wl_add_remove_eventmsg(bcmcfg_to_prmry_ndev(cfg),
                                   WLC_E_P2P_PROBREQ_MSG, false) != BCME_OK) {
            CFGP2P_ERR((" failed to unset WLC_E_P2P_PROPREQ_MSG\n"));
        }
    } else {
        wl_clr_p2p_status(cfg, LISTEN_EXPIRED);
    }

    return ret;
}

/*
 *  Timer expire callback function for LISTEN
 *  We can't report cfg80211_remain_on_channel_expired from Timer ISR context,
 *  so lets do it from thread context.
 */
void wl_cfgp2p_listen_expired(unsigned long data)
{
    wl_event_msg_t msg;
    struct bcm_cfg80211 *cfg = (struct bcm_cfg80211 *)data;
    struct net_device *ndev;
    CFGP2P_DBG((" Enter\n"));

    if (!cfg) {
        CFGP2P_ERR((" No cfg\n"));
        return;
    }
    bzero(&msg, sizeof(wl_event_msg_t));
    msg.event_type = hton32(WLC_E_P2P_DISC_LISTEN_COMPLETE);
    msg.bsscfgidx = wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_DEVICE);
#if defined(WL_ENABLE_P2P_IF)
    ndev = cfg->p2p_net ? cfg->p2p_net
                        : wl_to_p2p_bss_ndev(cfg, P2PAPI_BSSCFG_DEVICE);
#else
    ndev = wl_to_p2p_bss_ndev(cfg, P2PAPI_BSSCFG_DEVICE);
#endif /* WL_ENABLE_P2P_IF */
    if (!ndev) {
        CFGP2P_ERR((" No ndev\n"));
        return;
    }
    wl_cfg80211_event(ndev, &msg, NULL);
}
/*
 *  Routine for cancelling the P2P LISTEN
 */
static s32 wl_cfgp2p_cancel_listen(struct bcm_cfg80211 *cfg,
                                   struct net_device *ndev,
                                   struct wireless_dev *wdev, bool notify)
{
    WL_DBG(("Enter \n"));
    /* Irrespective of whether timer is running or not, reset
     * the LISTEN state.
     */
#ifdef NOT_YET
    wl_cfgp2p_set_p2p_mode(cfg, WL_P2P_DISC_ST_SCAN, 0, 0,
                           wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_DEVICE));
#endif /* NOT_YET */
    if (timer_pending(&cfg->p2p->listen_timer)) {
        del_timer_sync(&cfg->p2p->listen_timer);
        if (notify) {
#if defined(WL_CFG80211_P2P_DEV_IF)
            if (bcmcfg_to_p2p_wdev(cfg)) {
                cfg80211_remain_on_channel_expired(
                    wdev, cfg->last_roc_id, &cfg->remain_on_chan, GFP_KERNEL);
            }
#else
            if (ndev && ndev->ieee80211_ptr) {
                cfg80211_remain_on_channel_expired(
                    ndev, cfg->last_roc_id, &cfg->remain_on_chan,
                    cfg->remain_on_chan_type, GFP_KERNEL);
            }
#endif /* WL_CFG80211_P2P_DEV_IF */

#ifdef CONFIG_AP6XXX_WIFI6_HDF
            int ret = 0;
            ret = HdfWifiEventCancelRemainOnChannel(
                get_hdf_netdev(g_event_ifidx), cfg->remain_on_chan.center_freq);
            WL_ERR(("HdfWifiEventCancelRemainOnChannel ret=%d\n", ret));
#endif
        }
    }
    return 0;
}
/*
 * Do a P2P Listen on the given channel for the given duration.
 * A listen consists of sitting idle and responding to P2P probe requests
 * with a P2P probe response.
 *
 * This fn assumes dongle p2p device discovery is already enabled.
 * Parameters
 * @cfg          : wl_private data
 * @channel     : channel to listen
 * @duration_ms : the time (milli seconds) to wait
 */
s32 wl_cfgp2p_discover_listen(struct bcm_cfg80211 *cfg, s32 channel,
                              u32 duration_ms)
{
#define EXTRA_DELAY_TIME 100
    s32 ret = BCME_OK;
    timer_list_compat_t *_timer;
    s32 extra_delay;
    struct net_device *netdev = bcmcfg_to_prmry_ndev(cfg);

    CFGP2P_DBG(
        (" Enter Listen Channel : %d, Duration : %d\n", channel, duration_ms));
    if (unlikely(wl_get_p2p_status(cfg, DISCOVERY_ON) == 0)) {
        CFGP2P_ERR((" Discovery is not set, so we have noting to do\n"));

        ret = BCME_NOTREADY;
        goto exit;
    }
    if (timer_pending(&cfg->p2p->listen_timer)) {
        CFGP2P_DBG(("previous LISTEN is not completed yet\n"));
        goto exit;
    }
#ifndef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST
    else
        wl_clr_p2p_status(cfg, LISTEN_EXPIRED);
#endif /* not WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */
    if (wl_add_remove_eventmsg(netdev, WLC_E_P2P_PROBREQ_MSG, true) !=
        BCME_OK) {
        CFGP2P_ERR((" failed to set WLC_E_P2P_PROPREQ_MSG\n"));
    }

    ret = wl_cfgp2p_set_p2p_mode(
        cfg, WL_P2P_DISC_ST_LISTEN, channel, (u16)duration_ms,
        wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_DEVICE));
    _timer = &cfg->p2p->listen_timer;

    /*  We will wait to receive WLC_E_P2P_DISC_LISTEN_COMPLETE from dongle ,
     *  otherwise we will wait up to duration_ms + 100ms + duration / 10
     */
    if (ret == BCME_OK) {
        extra_delay = EXTRA_DELAY_TIME + (duration_ms / 0xA);
    } else {
        /* if failed to set listen, it doesn't need to wait whole duration. */
        duration_ms = 0x64 + duration_ms / 0x14;
        extra_delay = 0;
    }

    INIT_TIMER(_timer, wl_cfgp2p_listen_expired, duration_ms, extra_delay);
#ifdef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST
    wl_clr_p2p_status(cfg, LISTEN_EXPIRED);
#endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */

#undef EXTRA_DELAY_TIME
exit:
    return ret;
}

s32 wl_cfgp2p_discover_enable_search(struct bcm_cfg80211 *cfg, u8 enable)
{
    s32 ret = BCME_OK;
    CFGP2P_DBG((" Enter\n"));
    if (!wl_get_p2p_status(cfg, DISCOVERY_ON)) {
        CFGP2P_DBG((" do nothing, discovery is off\n"));
        return ret;
    }
    if (wl_get_p2p_status(cfg, SEARCH_ENABLED) == enable) {
        CFGP2P_DBG(("already : %d\n", enable));
        return ret;
    }

    wl_chg_p2p_status(cfg, SEARCH_ENABLED);
    /* When disabling Search, reset the WL driver's p2p discovery state to
     * WL_P2P_DISC_ST_SCAN.
     */
    if (!enable) {
        wl_clr_p2p_status(cfg, SCANNING);
        ret = wl_cfgp2p_set_p2p_mode(
            cfg, WL_P2P_DISC_ST_SCAN, 0, 0,
            wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_DEVICE));
    }
    return ret;
}

/*
 * Callback function for WLC_E_ACTION_FRAME_COMPLETE,
 * WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE
 */
s32 wl_cfgp2p_action_tx_complete(struct bcm_cfg80211 *cfg,
                                 bcm_struct_cfgdev *cfgdev,
                                 const wl_event_msg_t *e, void *data)
{
    s32 ret = BCME_OK;
    u32 event_type = ntoh32(e->event_type);
    u32 status = ntoh32(e->status);
    struct net_device *ndev = NULL;
    u8 bsscfgidx = e->bsscfgidx;

    CFGP2P_DBG((" Enter\n"));

    ndev = cfgdev_to_wlc_ndev(cfgdev, cfg);

    if (wl_get_drv_status_all(cfg, SENDING_ACT_FRM)) {
        if (event_type == WLC_E_ACTION_FRAME_COMPLETE) {
            CFGP2P_DBG(
                (" WLC_E_ACTION_FRAME_COMPLETE is received : %d\n", status));
            if (status == WLC_E_STATUS_SUCCESS) {
                wl_set_p2p_status(cfg, ACTION_TX_COMPLETED);
                CFGP2P_ACTION(("TX actfrm : ACK\n"));
                if (!cfg->need_wait_afrx && cfg->af_sent_channel) {
                    CFGP2P_DBG(("no need to wait next AF.\n"));
                    wl_stop_wait_next_action_frame(cfg, ndev, bsscfgidx);
                }
            } else if (!wl_get_p2p_status(cfg, ACTION_TX_COMPLETED)) {
                wl_set_p2p_status(cfg, ACTION_TX_NOACK);
                if (status == WLC_E_STATUS_SUPPRESS) {
                    CFGP2P_ACTION(("TX actfrm : SUPPRES\n"));
                } else {
                    CFGP2P_ACTION(("TX actfrm : NO ACK\n"));
                }
                wl_stop_wait_next_action_frame(cfg, ndev, bsscfgidx);
            }
        } else {
            CFGP2P_DBG((" WLC_E_ACTION_FRAME_OFFCHAN_COMPLETE is received,"
                        "status : %d\n",
                        status));

            if (wl_get_drv_status_all(cfg, SENDING_ACT_FRM)) {
                complete(&cfg->send_af_done);
            }
        }
    }
    return ret;
}
/* Send an action frame immediately without doing channel synchronization.
 *
 * This function does not wait for a completion event before returning.
 * The WLC_E_ACTION_FRAME_COMPLETE event will be received when the action
 * frame is transmitted.
 * The WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE event will be received when an
 * 802.11 ack has been received for the sent action frame.
 */
s32 wl_cfgp2p_tx_action_frame(struct bcm_cfg80211 *cfg, struct net_device *dev,
                              wl_af_params_t *af_params, s32 bssidx)
{
    s32 ret = BCME_OK;
    s32 evt_ret = BCME_OK;
    s32 timeout = 0;
    wl_eventmsg_buf_t buf;

    CFGP2P_DBG(("\n"));
    CFGP2P_DBG(("channel : %u , dwell time : %u\n", af_params->channel,
                af_params->dwell_time));

    wl_clr_p2p_status(cfg, ACTION_TX_COMPLETED);
    wl_clr_p2p_status(cfg, ACTION_TX_NOACK);

    bzero(&buf, sizeof(wl_eventmsg_buf_t));
    wl_cfg80211_add_to_eventbuffer(&buf, WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
                                   true);
    wl_cfg80211_add_to_eventbuffer(&buf, WLC_E_ACTION_FRAME_COMPLETE, true);
    if ((evt_ret = wl_cfg80211_apply_eventbuffer(bcmcfg_to_prmry_ndev(cfg), cfg,
                                                 &buf)) < 0) {
        return evt_ret;
    }

    cfg->af_sent_channel = af_params->channel;
#ifdef WL_CFG80211_SYNC_GON
    cfg->af_tx_sent_jiffies = jiffies;
#endif /* WL_CFG80211_SYNC_GON */

    ret = wldev_iovar_setbuf_bsscfg(
        dev, "actframe", af_params, sizeof(*af_params), cfg->ioctl_buf,
        WLC_IOCTL_MAXLEN, bssidx, &cfg->ioctl_buf_sync);
    if (ret < 0) {
        CFGP2P_ACTION(("TX actfrm : ERROR\n"));
        goto exit;
    }

    timeout = wait_for_completion_timeout(
        &cfg->send_af_done,
        msecs_to_jiffies(af_params->dwell_time + WL_AF_TX_EXTRA_TIME_MAX));
    if (timeout >= 0 && wl_get_p2p_status(cfg, ACTION_TX_COMPLETED)) {
        CFGP2P_DBG(("tx action frame operation is completed\n"));
        ret = BCME_OK;
    } else if (ETHER_ISBCAST(&cfg->afx_hdl->tx_dst_addr)) {
        CFGP2P_DBG(("bcast tx action frame operation is completed\n"));
        ret = BCME_OK;
    } else {
        ret = BCME_ERROR;
        CFGP2P_DBG(("tx action frame operation is failed\n"));
    }
    /* clear status bit for action tx */
    wl_clr_p2p_status(cfg, ACTION_TX_COMPLETED);
    wl_clr_p2p_status(cfg, ACTION_TX_NOACK);

exit:
    CFGP2P_DBG((" via act frame iovar : status = %d\n", ret));

    bzero(&buf, sizeof(wl_eventmsg_buf_t));
    wl_cfg80211_add_to_eventbuffer(&buf, WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
                                   false);
    wl_cfg80211_add_to_eventbuffer(&buf, WLC_E_ACTION_FRAME_COMPLETE, false);
    if ((evt_ret = wl_cfg80211_apply_eventbuffer(bcmcfg_to_prmry_ndev(cfg), cfg,
                                                 &buf)) < 0) {
        WL_ERR(("TX frame events revert back failed \n"));
        return evt_ret;
    }

    return ret;
}

/* Generate our P2P Device Address and P2P Interface Address from our primary
 * MAC address.
 */
void wl_cfgp2p_generate_bss_mac(struct bcm_cfg80211 *cfg,
                                struct ether_addr *primary_addr)
{
    struct ether_addr *mac_addr =
        wl_to_p2p_bss_macaddr(cfg, P2PAPI_BSSCFG_DEVICE);
    struct ether_addr *int_addr;
#ifdef P2P_AP_CONCURRENT
    dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
#endif

#ifdef WL_P2P_USE_RANDMAC
    dhd_generate_mac_addr(mac_addr);
#else
    memcpy(mac_addr, primary_addr, sizeof(struct ether_addr));
    mac_addr->octet[0] |= 0x02;
#ifdef P2P_AP_CONCURRENT
    if (dhd->conf->war & P2P_AP_MAC_CONFLICT) {
        wl_ext_iapsta_get_vif_macaddr(dhd, 0x2, (u8 *)mac_addr);
    }
#endif
    WL_DBG(("P2P Discovery address:" MACDBG "\n", MAC2STRDBG(mac_addr->octet)));
#endif /* WL_P2P_USE_RANDMAC */

    int_addr = wl_to_p2p_bss_macaddr(cfg, P2PAPI_BSSCFG_CONNECTION1);
    memcpy(int_addr, mac_addr, sizeof(struct ether_addr));
    int_addr->octet[0x4] ^= 0x80;
    WL_DBG(("Primary P2P Interface address:" MACDBG "\n",
            MAC2STRDBG(int_addr->octet)));

    int_addr = wl_to_p2p_bss_macaddr(cfg, P2PAPI_BSSCFG_CONNECTION2);
    memcpy(int_addr, mac_addr, sizeof(struct ether_addr));
    int_addr->octet[0x4] ^= 0x90;
}

/* P2P IF Address change to Virtual Interface MAC Address */
void wl_cfg80211_change_ifaddr(u8 *buf, struct ether_addr *p2p_int_addr,
                               u8 element_id)
{
    wifi_p2p_ie_t *ie = (wifi_p2p_ie_t *)buf;
    u16 len = ie->len;
    u8 *subel;
    u8 subelt_id;
    u16 subelt_len;
    CFGP2P_DBG((" Enter\n"));

    /* Point subel to the P2P IE's subelt field.
     * Subtract the preceding fields (id, len, OUI, oui_type) from the length.
     */
    subel = ie->subelts;
    len -= 0x4; /* exclude OUI + OUI_TYPE */

    while (len >= 0x3) {
        /* attribute id */
        subelt_id = *subel;
        subel += 1;
        len -= 1;

        /* 2-byte little endian */
        subelt_len = *subel++;
        subelt_len |= *subel++ << 0x8;

        len -= 0x2;
        len -= subelt_len; /* for the remaining subelt fields */

        if (subelt_id == element_id) {
            if (subelt_id == P2P_SEID_INTINTADDR) {
                memcpy(subel, p2p_int_addr->octet, ETHER_ADDR_LEN);
                CFGP2P_INFO(("Intended P2P Interface Address ATTR FOUND\n"));
            } else if (subelt_id == P2P_SEID_DEV_ID) {
                memcpy(subel, p2p_int_addr->octet, ETHER_ADDR_LEN);
                CFGP2P_INFO(("Device ID ATTR FOUND\n"));
            } else if (subelt_id == P2P_SEID_DEV_INFO) {
                memcpy(subel, p2p_int_addr->octet, ETHER_ADDR_LEN);
                CFGP2P_INFO(("Device INFO ATTR FOUND\n"));
            } else if (subelt_id == P2P_SEID_GROUP_ID) {
                memcpy(subel, p2p_int_addr->octet, ETHER_ADDR_LEN);
                CFGP2P_INFO(("GROUP ID ATTR FOUND\n"));
            }
            return;
        } else {
            CFGP2P_DBG(("OTHER id : %d\n", subelt_id));
        }
        subel += subelt_len;
    }
}

s32 wl_cfgp2p_supported(struct bcm_cfg80211 *cfg, struct net_device *ndev)
{
    s32 ret = BCME_OK;
    s32 p2p_supported = 0;
    ret = wldev_iovar_getint(ndev, "p2p", &p2p_supported);
    if (ret < 0) {
        if (ret == BCME_UNSUPPORTED) {
            CFGP2P_INFO(("p2p is unsupported\n"));
            return 0;
        } else {
            CFGP2P_ERR(("cfg p2p error %d\n", ret));
            return ret;
        }
    }
    if (cfg->pub->conf->fw_type == FW_TYPE_MESH) {
        p2p_supported = 0;
    }
    if (p2p_supported == 1) {
        CFGP2P_INFO(("p2p is supported\n"));
    } else {
        CFGP2P_INFO(("p2p is unsupported\n"));
        p2p_supported = 0;
    }
    return p2p_supported;
}

/* Cleanup P2P resources */
s32 wl_cfgp2p_down(struct bcm_cfg80211 *cfg)
{
    struct net_device *ndev = NULL;
    struct wireless_dev *wdev = NULL;

#if defined(WL_CFG80211_P2P_DEV_IF)
    ndev = bcmcfg_to_prmry_ndev(cfg);
    wdev = bcmcfg_to_p2p_wdev(cfg);
#elif defined(WL_ENABLE_P2P_IF)
    ndev = cfg->p2p_net ? cfg->p2p_net : bcmcfg_to_prmry_ndev(cfg);
    wdev = ndev_to_wdev(ndev);
#endif /* WL_CFG80211_P2P_DEV_IF */

    wl_cfgp2p_cancel_listen(cfg, ndev, wdev, TRUE);
    wl_cfgp2p_disable_discovery(cfg);

#if defined(WL_CFG80211_P2P_DEV_IF) && !defined(KEEP_WIFION_OPTION)
    /*
     * In CUSTOMER_HW4 implementation "ifconfig wlan0 down" can get
     * called during phone suspend and customer requires the p2p
     * discovery interface to be left untouched so that the user
     * space can resume without any problem.
     */
    if (cfg->p2p_wdev) {
        /* If p2p wdev is left out, clean it up */
        WL_ERR(("Clean up the p2p discovery IF\n"));
        wl_cfgp2p_del_p2p_disc_if(cfg->p2p_wdev, cfg);
    }
#endif /* WL_CFG80211_P2P_DEV_IF !defined(KEEP_WIFION_OPTION) */

    wl_cfgp2p_deinit_priv(cfg);
    return 0;
}

int wl_cfgp2p_vif_created(struct bcm_cfg80211 *cfg)
{
    if (cfg->p2p &&
        ((wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_CONNECTION1) != -1) ||
         (wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_CONNECTION2) != -1))) {
        return true;
    } else {
        return false;
    }
}

s32 wl_cfgp2p_set_p2p_noa(struct bcm_cfg80211 *cfg, struct net_device *ndev,
                          char *buf, int len)
{
    s32 ret = -1;
    int count, start, duration;
    wl_p2p_sched_t dongle_noa;
    s32 bssidx, type;
    int iovar_len = sizeof(dongle_noa);
    CFGP2P_DBG((" Enter\n"));

    bzero(&dongle_noa, sizeof(dongle_noa));

    if (wl_cfgp2p_vif_created(cfg)) {
        cfg->p2p->noa.desc[0].start = 0;

        sscanf(buf, "%10d %10d %10d", &count, &start, &duration);
        CFGP2P_DBG(("set_p2p_noa count %d start %d duration %d\n", count, start,
                    duration));
        if (count != -1) {
            cfg->p2p->noa.desc[0].count = count;
        }

        /* supplicant gives interval as start */
        if (start != -1) {
            cfg->p2p->noa.desc[0].interval = start;
        }

        if (duration != -1) {
            cfg->p2p->noa.desc[0].duration = duration;
        }

        if (cfg->p2p->noa.desc[0].count != 0xFF &&
            cfg->p2p->noa.desc[0].count != 0) {
            cfg->p2p->noa.desc[0].start = 0xC8;
            dongle_noa.type = WL_P2P_SCHED_TYPE_REQ_ABS;
            dongle_noa.action = WL_P2P_SCHED_ACTION_GOOFF;
            dongle_noa.option = WL_P2P_SCHED_OPTION_TSFOFS;
        } else if (cfg->p2p->noa.desc[0].count == 0) {
            cfg->p2p->noa.desc[0].start = 0;
            dongle_noa.type = WL_P2P_SCHED_TYPE_ABS;
            dongle_noa.option = WL_P2P_SCHED_OPTION_NORMAL;
            dongle_noa.action = WL_P2P_SCHED_ACTION_RESET;
        } else {
            /* Continuous NoA interval. */
            dongle_noa.action = WL_P2P_SCHED_ACTION_DOZE;
            dongle_noa.type = WL_P2P_SCHED_TYPE_ABS;
            if ((cfg->p2p->noa.desc[0].interval == 0x66) ||
                (cfg->p2p->noa.desc[0].interval == 0x64)) {
                cfg->p2p->noa.desc[0].start =
                    0x64 - cfg->p2p->noa.desc[0].duration;
                dongle_noa.option = WL_P2P_SCHED_OPTION_BCNPCT;
            } else {
                dongle_noa.option = WL_P2P_SCHED_OPTION_NORMAL;
            }
        }
        /* Put the noa descriptor in dongle format for dongle */
        dongle_noa.desc[0].count = htod32(cfg->p2p->noa.desc[0].count);
        if (dongle_noa.option == WL_P2P_SCHED_OPTION_BCNPCT) {
            dongle_noa.desc[0].start = htod32(cfg->p2p->noa.desc[0].start);
            dongle_noa.desc[0].duration =
                htod32(cfg->p2p->noa.desc[0].duration);
        } else {
            dongle_noa.desc[0].start =
                htod32(cfg->p2p->noa.desc[0].start * 0x3E8);
            dongle_noa.desc[0].duration =
                htod32(cfg->p2p->noa.desc[0].duration * 0x3E8);
        }
        dongle_noa.desc[0].interval =
            htod32(cfg->p2p->noa.desc[0].interval * 0x3E8);
        bssidx = wl_get_bssidx_by_wdev(cfg, ndev->ieee80211_ptr);
        if (wl_cfgp2p_find_type(cfg, bssidx, &type) != BCME_OK) {
            return BCME_ERROR;
        }

        if (dongle_noa.action == WL_P2P_SCHED_ACTION_RESET) {
            iovar_len -= sizeof(wl_p2p_sched_desc_t);
        }

        ret = wldev_iovar_setbuf(wl_to_p2p_bss_ndev(cfg, type), "p2p_noa",
                                 &dongle_noa, iovar_len, cfg->ioctl_buf,
                                 WLC_IOCTL_MAXLEN, &cfg->ioctl_buf_sync);
        if (ret < 0) {
            CFGP2P_ERR(("fw set p2p_noa failed %d\n", ret));
        }
    } else {
        CFGP2P_ERR(("ERROR: set_noa in non-p2p mode\n"));
    }
    return ret;
}
s32 wl_cfgp2p_get_p2p_noa(struct bcm_cfg80211 *cfg, struct net_device *ndev,
                          char *buf, int buf_len)
{
    wifi_p2p_noa_desc_t *noa_desc;
    int len = 0, i;
    char _buf[200];

    CFGP2P_DBG((" Enter\n"));
    buf[0] = '\0';
    if (wl_cfgp2p_vif_created(cfg)) {
        if (cfg->p2p->noa.desc[0].count || cfg->p2p->ops.ops) {
            _buf[0] = 1; /* noa index */
            _buf[1] = (cfg->p2p->ops.ops ? 0x80 : 0) |
                      (cfg->p2p->ops.ctw & 0x7f); /* ops + ctw */
            len += 0x2;
            if (cfg->p2p->noa.desc[0].count) {
                noa_desc = (wifi_p2p_noa_desc_t *)&_buf[len];
                noa_desc->cnt_type = cfg->p2p->noa.desc[0].count;
                noa_desc->duration = cfg->p2p->noa.desc[0].duration;
                noa_desc->interval = cfg->p2p->noa.desc[0].interval;
                noa_desc->start = cfg->p2p->noa.desc[0].start;
                len += sizeof(wifi_p2p_noa_desc_t);
            }
            if (buf_len <= len * 0x2) {
                CFGP2P_ERR(("ERROR: buf_len %d in not enough for"
                            "returning noa in string format\n",
                            buf_len));
                return -1;
            }
            /* We have to convert the buffer data into ASCII strings */
            for (i = 0; i < len; i++) {
                snprintf(buf, 0x3, "%02x", _buf[i]);
                buf += 0x2;
            }
            buf[i * 0x2] = '\0';
        }
    } else {
        CFGP2P_ERR(("ERROR: get_noa in non-p2p mode\n"));
        return -1;
    }
    return len * 0x2;
}
s32 wl_cfgp2p_set_p2p_ps(struct bcm_cfg80211 *cfg, struct net_device *ndev,
                         char *buf, int len)
{
    int ps, ctw;
    int ret = -1;
    s32 legacy_ps;
    s32 conn_idx;
    s32 bssidx;
    struct net_device *dev;

    CFGP2P_DBG((" Enter\n"));
    if (wl_cfgp2p_vif_created(cfg)) {
        sscanf(buf, "%10d %10d %10d", &legacy_ps, &ps, &ctw);
        CFGP2P_DBG((" Enter legacy_ps %d ps %d ctw %d\n", legacy_ps, ps, ctw));

        bssidx = wl_get_bssidx_by_wdev(cfg, ndev->ieee80211_ptr);
        if (wl_cfgp2p_find_type(cfg, bssidx, &conn_idx) != BCME_OK) {
            return BCME_ERROR;
        }
        dev = wl_to_p2p_bss_ndev(cfg, conn_idx);
        if (ctw != -1) {
            cfg->p2p->ops.ctw = ctw;
            ret = 0;
        }
        if (ps != -1) {
            cfg->p2p->ops.ops = ps;
            ret = wldev_iovar_setbuf(dev, "p2p_ops", &cfg->p2p->ops,
                                     sizeof(cfg->p2p->ops), cfg->ioctl_buf,
                                     WLC_IOCTL_MAXLEN, &cfg->ioctl_buf_sync);
            if (ret < 0) {
                CFGP2P_ERR(("fw set p2p_ops failed %d\n", ret));
            }
        }

        if ((legacy_ps != -1) &&
            ((legacy_ps == PM_MAX) || (legacy_ps == PM_OFF))) {
            ret =
                wldev_ioctl_set(dev, WLC_SET_PM, &legacy_ps, sizeof(legacy_ps));
            if (unlikely(ret)) {
                CFGP2P_ERR(("error (%d)\n", ret));
            }
            wl_cfg80211_update_power_mode(dev);
        } else {
            CFGP2P_ERR(("ilegal setting\n"));
        }
    } else {
        CFGP2P_ERR(("ERROR: set_p2p_ps in non-p2p mode\n"));
        ret = -1;
    }
    return ret;
}

s32 wl_cfgp2p_set_p2p_ecsa(struct bcm_cfg80211 *cfg, struct net_device *ndev,
                           char *buf, int len)
{
    int ch, bw;
    s32 conn_idx;
    s32 bssidx;
    struct net_device *dev;
    char smbuf[WLC_IOCTL_SMLEN];
    wl_chan_switch_t csa_arg;
    u32 chnsp = 0;
    int err = 0;

    CFGP2P_DBG((" Enter\n"));
    if (wl_cfgp2p_vif_created(cfg)) {
        sscanf(buf, "%10d %10d", &ch, &bw);
        CFGP2P_DBG(("Enter ch %d bw %d\n", ch, bw));

        bssidx = wl_get_bssidx_by_wdev(cfg, ndev->ieee80211_ptr);
        if (wl_cfgp2p_find_type(cfg, bssidx, &conn_idx) != BCME_OK) {
            return BCME_ERROR;
        }
        dev = wl_to_p2p_bss_ndev(cfg, conn_idx);
        if (ch <= 0 || bw <= 0) {
            CFGP2P_ERR(("Negative value not permitted!\n"));
            return BCME_ERROR;
        }

        memset_s(&csa_arg, sizeof(csa_arg), 0, sizeof(csa_arg));
        csa_arg.mode = DOT11_CSA_MODE_ADVISORY;
        csa_arg.count = P2P_ECSA_CNT;
        csa_arg.reg = 0;

        snprintf(buf, len, "%d/%d", ch, bw);
        chnsp = wf_chspec_aton(buf);
        if (chnsp == 0) {
            CFGP2P_ERR(("%s:chsp is not correct\n", __FUNCTION__));
            return BCME_ERROR;
        }
        chnsp = wl_chspec_host_to_driver(chnsp);
        csa_arg.chspec = chnsp;

        err = wldev_iovar_setbuf(dev, "csa", &csa_arg, sizeof(csa_arg), smbuf,
                                 sizeof(smbuf), NULL);
        if (err) {
            CFGP2P_ERR(("%s:set p2p_ecsa failed:%d\n", __FUNCTION__, err));
            return BCME_ERROR;
        }
    } else {
        CFGP2P_ERR(("ERROR: set_p2p_ecsa in non-p2p mode\n"));
        return BCME_ERROR;
    }
    return BCME_OK;
}

s32 wl_cfgp2p_increase_p2p_bw(struct bcm_cfg80211 *cfg, struct net_device *ndev,
                              char *buf, int len)
{
    int algo;
    int bw;
    int ret = BCME_OK;

    sscanf(buf, "%3d", &bw);
    if (bw == 0) {
        algo = 0;
        ret = wldev_iovar_setbuf(ndev, "mchan_algo", &algo, sizeof(algo),
                                 cfg->ioctl_buf, WLC_IOCTL_MAXLEN,
                                 &cfg->ioctl_buf_sync);
        if (ret < 0) {
            CFGP2P_ERR(("fw set mchan_algo failed %d\n", ret));
            return BCME_ERROR;
        }
    } else {
        algo = 1;
        ret = wldev_iovar_setbuf(ndev, "mchan_algo", &algo, sizeof(algo),
                                 cfg->ioctl_buf, WLC_IOCTL_MAXLEN,
                                 &cfg->ioctl_buf_sync);
        if (ret < 0) {
            CFGP2P_ERR(("fw set mchan_algo failed %d\n", ret));
            return BCME_ERROR;
        }
        ret = wldev_iovar_setbuf(ndev, "mchan_bw", &bw, sizeof(algo),
                                 cfg->ioctl_buf, WLC_IOCTL_MAXLEN,
                                 &cfg->ioctl_buf_sync);
        if (ret < 0) {
            CFGP2P_ERR(("fw set mchan_bw failed %d\n", ret));
            return BCME_ERROR;
        }
    }
    return BCME_OK;
}

const u8 *wl_cfgp2p_retreive_p2pattrib(const void *buf, u8 element_id)
{
    const wifi_p2p_ie_t *ie = NULL;
    u16 len = 0;
    const u8 *subel;
    u8 subelt_id;
    u16 subelt_len;

    if (!buf) {
        WL_ERR(("P2P IE not present"));
        return 0;
    }

    ie = (const wifi_p2p_ie_t *)buf;
    len = ie->len;

    /* Point subel to the P2P IE's subelt field.
     * Subtract the preceding fields (id, len, OUI, oui_type) from the length.
     */
    subel = ie->subelts;
    len -= 0x4; /* exclude OUI + OUI_TYPE */

    while (len >= 0x3) {
        /* attribute id */
        subelt_id = *subel;
        subel += 1;
        len -= 1;

        /* 2-byte little endian */
        subelt_len = *subel++;
        subelt_len |= *subel++ << 0x8;

        len -= 0x2;
        len -= subelt_len; /* for the remaining subelt fields */

        if (subelt_id == element_id) {
            /* This will point to start of subelement attrib after
             * attribute id & len
             */
            return subel;
        }

        /* Go to next subelement */
        subel += subelt_len;
    }

    /* Not Found */
    return NULL;
}

#define P2P_GROUP_CAPAB_GO_BIT 0x01

const u8 *wl_cfgp2p_find_attrib_in_all_p2p_Ies(const u8 *parse, u32 len,
                                               u32 attrib)
{
    bcm_tlv_t *ie;
    const u8 *pAttrib;
    uint ie_len;

    CFGP2P_DBG(("Starting parsing parse %p attrib %d remaining len %d ", parse,
                attrib, len));
    ie_len = len;
    while ((ie = bcm_parse_tlvs(parse, ie_len, DOT11_MNG_VS_ID))) {
        if (wl_cfgp2p_is_p2p_ie(ie, &parse, &ie_len) == TRUE) {
            /* Have the P2p ie. Now check for attribute */
            if ((pAttrib = wl_cfgp2p_retreive_p2pattrib(ie, attrib)) != NULL) {
                CFGP2P_DBG(
                    ("P2P attribute %d was found at parse %p", attrib, parse));
                return pAttrib;
            } else {
                /* move to next IE */
                bcm_tlv_buffer_advance_past(ie, &parse, &ie_len);

                CFGP2P_INFO(("P2P Attribute %d not found Moving parse"
                             " to %p len to %d",
                             attrib, parse, ie_len));
            }
        } else {
            /* It was not p2p IE. parse will get updated automatically to next
             * TLV */
            CFGP2P_INFO(("IT was NOT P2P IE parse %p len %d", parse, ie_len));
        }
    }
    CFGP2P_ERR(("P2P attribute %d was NOT found", attrib));
    return NULL;
}

const u8 *wl_cfgp2p_retreive_p2p_dev_addr(wl_bss_info_t *bi, u32 bi_length)
{
    const u8 *capability = NULL;
    bool p2p_go = 0;
    const u8 *ptr = NULL;

    if (bi->length != bi->ie_offset + bi->ie_length) {
        return NULL;
    }

    if ((capability = wl_cfgp2p_find_attrib_in_all_p2p_Ies(
             ((u8 *)bi) + bi->ie_offset, bi->ie_length, P2P_SEID_P2P_INFO)) == NULL) {
        WL_ERR(("P2P Capability attribute not found"));
        return NULL;
    }

    /* Check Group capability for Group Owner bit */
    p2p_go = capability[1] & P2P_GROUP_CAPAB_GO_BIT;
    if (!p2p_go) {
        return bi->BSSID.octet;
    }

    /* In probe responses, DEVICE INFO attribute will be present */
    if (!(ptr = wl_cfgp2p_find_attrib_in_all_p2p_Ies(
              ((u8 *)bi) + bi->ie_offset, bi->ie_length, P2P_SEID_DEV_INFO))) {
        /* If DEVICE_INFO is not found, this might be a beacon frame.
         * check for DEVICE_ID in the beacon frame.
         */
        ptr = wl_cfgp2p_find_attrib_in_all_p2p_Ies(
            ((u8 *)bi) + bi->ie_offset, bi->ie_length, P2P_SEID_DEV_ID);
    }

    if (!ptr) {
        WL_ERR(
            (" Both DEVICE_ID & DEVICE_INFO attribute not present in P2P IE "));
    }

    return ptr;
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
static void wl_cfgp2p_ethtool_get_drvinfo(struct net_device *net,
                                          struct ethtool_drvinfo *info)
{
    snprintf(info->driver, sizeof(info->driver), "p2p");
    snprintf(info->version, sizeof(info->version), "%lu", (unsigned long)(0));
}

struct ethtool_ops cfgp2p_ethtool_ops = {.get_drvinfo =
                                             wl_cfgp2p_ethtool_get_drvinfo};
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */

#ifdef CONFIG_AP6XXX_WIFI6_HDF
int hdf_cfgp2p_register_ndev(struct net_device *p2p_netdev,
                             struct net_device *primary_netdev,
                             struct wiphy *wiphy)
{
    WL_MSG(p2p_netdev->name, "P2P Interface Registered\n");
    return 0;
}
#endif

#if defined(WL_ENABLE_P2P_IF)
s32 wl_cfgp2p_register_ndev(struct bcm_cfg80211 *cfg)
{
    int ret = 0;
    struct net_device *net = NULL;
    struct wireless_dev *wdev = NULL;
    uint8 temp_addr[ETHER_ADDR_LEN] = {0x00, 0x90, 0x4c, 0x33, 0x22, 0x11};

    if (cfg->p2p_net) {
        CFGP2P_ERR(("p2p_net defined already.\n"));
        return -EINVAL;
    }

    /* Allocate etherdev, including space for private structure */
    if (!(net = alloc_etherdev(sizeof(struct bcm_cfg80211 *)))) {
        CFGP2P_ERR(("%s: OOM - alloc_etherdev\n", __FUNCTION__));
        return -ENODEV;
    }

    wdev = kzalloc(sizeof(*wdev), GFP_KERNEL);
    if (unlikely(!wdev)) {
        WL_ERR(("Could not allocate wireless device\n"));
        free_netdev(net);
        return -ENOMEM;
    }

    strncpy(net->name, "p2p%d", sizeof(net->name) - 1);
    net->name[IFNAMSIZ - 1] = '\0';

    /* Copy the reference to bcm_cfg80211 */
    memcpy((void *)netdev_priv(net), &cfg, sizeof(struct bcm_cfg80211 *));

#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31))
    ASSERT(!net->open);
    net->do_ioctl = wl_cfgp2p_do_ioctl;
    net->hard_start_xmit = wl_cfgp2p_start_xmit;
    net->open = wl_cfgp2p_if_open;
    net->stop = wl_cfgp2p_if_stop;
#else
    ASSERT(!net->netdev_ops);
    net->netdev_ops = &wl_cfgp2p_if_ops;
#endif

    /* Register with a dummy MAC addr */
    memcpy(net->dev_addr, temp_addr, ETHER_ADDR_LEN);

    wdev->wiphy = cfg->wdev->wiphy;

    wdev->iftype = wl_mode_to_nl80211_iftype(WL_MODE_BSS);

    net->ieee80211_ptr = wdev;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
    net->ethtool_ops = &cfgp2p_ethtool_ops;
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */

    SET_NETDEV_DEV(net, wiphy_dev(wdev->wiphy));

    /* Associate p2p0 network interface with new wdev */
    wdev->netdev = net;

    ret = register_netdev(net);
    if (ret) {
        CFGP2P_ERR((" register_netdevice failed (%d)\n", ret));
        free_netdev(net);
        kfree(wdev);
        return -ENODEV;
    }

    /* store p2p net ptr for further reference. Note that iflist won't have this
     * entry as there corresponding firmware interface is a "Hidden" interface.
     */
    cfg->p2p_wdev = wdev;
    cfg->p2p_net = net;

    WL_MSG(net->name, "P2P Interface Registered\n");

    return ret;
}

s32 wl_cfgp2p_unregister_ndev(struct bcm_cfg80211 *cfg)
{
    if (!cfg || !cfg->p2p_net) {
        CFGP2P_ERR(("Invalid Ptr\n"));
        return -EINVAL;
    }

    unregister_netdev(cfg->p2p_net);
    free_netdev(cfg->p2p_net);

    return 0;
}
static netdev_tx_t wl_cfgp2p_start_xmit(struct sk_buff *skb,
                                        struct net_device *ndev)
{
    if (skb) {
        CFGP2P_DBG(
            ("(%s) is not used for data operations.Droping the packet.\n",
             ndev->name));
        dev_kfree_skb_any(skb);
    }

    return 0;
}

static int wl_cfgp2p_do_ioctl(struct net_device *net, struct ifreq *ifr,
                              int cmd)
{
    int ret = 0;
    struct bcm_cfg80211 *cfg = *(struct bcm_cfg80211 **)netdev_priv(net);
    struct net_device *ndev = bcmcfg_to_prmry_ndev(cfg);

    /* There is no ifidx corresponding to p2p0 in our firmware. So we should
     * not Handle any IOCTL cmds on p2p0 other than OHOS PRIVATE CMDs.
     * For OHOS PRIV CMD handling map it to primary I/F
     */
    if (cmd == SIOCDEVPRIVATE + 1) {
        ret = wl_ohos_priv_cmd(ndev, ifr);
    } else {
        CFGP2P_ERR(("%s: IOCTL req 0x%x on p2p0 I/F. Ignoring. \n",
                    __FUNCTION__, cmd));
        return -1;
    }
    return ret;
}
#endif

#if defined(WL_ENABLE_P2P_IF)
static int wl_cfgp2p_if_open(struct net_device *net)
{
    struct wireless_dev *wdev = net->ieee80211_ptr;

    if (!wdev || !wl_cfg80211_is_p2p_active(net)) {
        return -EINVAL;
    }
    WL_TRACE(("Enter\n"));
#if !defined(WL_IFACE_COMB_NUM_CHANNELS)
    /* If suppose F/W download (ifconfig wlan0 up) hasn't been done by now,
     * do it here. This will make sure that in concurrent mode, supplicant
     * is not dependent on a particular order of interface initialization.
     * i.e you may give wpa_supp -iwlan0 -N -ip2p0 or wpa_supp -ip2p0 -N
     * -iwlan0.
     */
    wdev->wiphy->interface_modes |=
        (BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO));
#endif /* !WL_IFACE_COMB_NUM_CHANNELS */
    wl_cfg80211_do_driver_init(net);

    return 0;
}

static int wl_cfgp2p_if_stop(struct net_device *net)
{
    struct wireless_dev *wdev = net->ieee80211_ptr;
    struct bcm_cfg80211 *cfg = wl_get_cfg(net);

    if (!wdev) {
        return -EINVAL;
    }

    wl_cfg80211_scan_stop(cfg, net);

#if !defined(WL_IFACE_COMB_NUM_CHANNELS)
    wdev->wiphy->interface_modes =
        (wdev->wiphy->interface_modes) &
        (~(BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO)));
#endif /* !WL_IFACE_COMB_NUM_CHANNELS */
    return 0;
}

bool wl_cfgp2p_is_ifops(const struct net_device_ops *if_ops)
{
    return (if_ops == &wl_cfgp2p_if_ops);
}
#endif /* WL_ENABLE_P2P_IF */

#if defined(WL_CFG80211_P2P_DEV_IF)
struct wireless_dev *wl_cfgp2p_add_p2p_disc_if(struct bcm_cfg80211 *cfg)
{
    struct wireless_dev *wdev = NULL;
    struct ether_addr primary_mac;

    if (!cfg || !cfg->p2p_supported) {
        return ERR_PTR(-EINVAL);
    }

    WL_TRACE(("Enter\n"));

    if (cfg->p2p_wdev) {
#ifndef EXPLICIT_DISCIF_CLEANUP
        dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
#endif /* EXPLICIT_DISCIF_CLEANUP */
        /*
         * This is not expected. This can happen due to
         * supplicant crash/unclean de-initialization which
         * didn't free the p2p discovery interface. Indicate
         * driver hang to user space so that the framework
         * can rei-init the Wi-Fi.
         */
        CFGP2P_ERR(("p2p_wdev defined already.\n"));
        wl_probe_wdev_all(cfg);
#ifdef EXPLICIT_DISCIF_CLEANUP
        /*
         * CUSTOMER_HW4 design doesn't delete the p2p discovery
         * interface on ifconfig wlan0 down context which comes
         * without a preceeding NL80211_CMD_DEL_INTERFACE for p2p
         * discovery. But during supplicant crash the DEL_IFACE
         * command will not happen and will cause a left over iface
         * even after ifconfig wlan0 down. So delete the iface
         * first and then indicate the HANG event
         */
        wl_cfgp2p_del_p2p_disc_if(cfg->p2p_wdev, cfg);
#else
        dhd->hang_reason = HANG_REASON_IFACE_DEL_FAILURE;
#if defined(BCMPCIE) && defined(DHD_FW_COREDUMP)
        if (dhd->memdump_enabled) {
            /* Load the dongle side dump to host
             * memory and then BUG_ON()
             */
            dhd->memdump_type = DUMP_TYPE_IFACE_OP_FAILURE;
            dhd_bus_mem_dump(dhd);
        }
#endif /* BCMPCIE && DHD_FW_COREDUMP */
        net_os_send_hang_message(bcmcfg_to_prmry_ndev(cfg));
        return ERR_PTR(-ENODEV);
#endif /* EXPLICIT_DISCIF_CLEANUP */
    }

    wdev = (struct wireless_dev *)MALLOCZ(cfg->osh, sizeof(*wdev));
    if (unlikely(!wdev)) {
        WL_ERR(("Could not allocate wireless device\n"));
        return ERR_PTR(-ENOMEM);
    }

    bzero(&primary_mac, sizeof(primary_mac));
    get_primary_mac(cfg, &primary_mac);
#ifndef WL_P2P_USE_RANDMAC
    wl_cfgp2p_generate_bss_mac(cfg, &primary_mac);
#endif /* WL_P2P_USE_RANDMAC */

    wdev->wiphy = cfg->wdev->wiphy;
    wdev->iftype = NL80211_IFTYPE_P2P_DEVICE;
    memcpy(wdev->address, wl_to_p2p_bss_macaddr(cfg, P2PAPI_BSSCFG_DEVICE),
           ETHER_ADDR_LEN);

    /* store p2p wdev ptr for further reference. */
    cfg->p2p_wdev = wdev;

    printf(
        "P2P interface registered wdev mac = %02x:%02x:%02x:%02x:%02x:%02x\n",
        wdev->address[0], wdev->address[1], wdev->address[0x2], wdev->address[0x3],
        wdev->address[0x4], wdev->address[0x5]);
    magiclink_add_p2p("p2p0", wl_to_p2p_bss_macaddr(cfg, P2PAPI_BSSCFG_DEVICE),
                      &p2p_debug_ndev);
    return wdev;
}

int wl_cfgp2p_start_p2p_device(struct wiphy *wiphy, struct wireless_dev *wdev)
{
    int ret = 0;
    struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);

    if (!cfg) {
        return -EINVAL;
    }

    WL_TRACE(("Enter\n"));

    ret = wl_cfgp2p_set_firm_p2p(cfg);
    if (unlikely(ret < 0)) {
        CFGP2P_ERR(("Set P2P in firmware failed, ret=%d\n", ret));
        goto exit;
    }

    ret = wl_cfgp2p_enable_discovery(cfg, bcmcfg_to_prmry_ndev(cfg), NULL, 0);
    if (unlikely(ret < 0)) {
        CFGP2P_ERR(("P2P enable discovery failed, ret=%d\n", ret));
        goto exit;
    }

    p2p_on(cfg) = true;
#if defined(P2P_IE_MISSING_FIX)
    cfg->p2p_prb_noti = false;
#endif // endif

    CFGP2P_DBG(("P2P interface started\n"));

exit:
    return ret;
}

void wl_cfgp2p_stop_p2p_device(struct wiphy *wiphy, struct wireless_dev *wdev)
{
    int ret = 0;
    struct net_device *ndev = NULL;
    struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);

    if (!cfg) {
        return;
    }

    CFGP2P_DBG(("Enter\n"));

    /* Check if cfg80211 interface is already down */
    ndev = bcmcfg_to_prmry_ndev(cfg);
    if (!wl_get_drv_status(cfg, READY, ndev)) {
        WL_DBG(("cfg80211 interface is already down\n"));
        return; /* it is even not ready */
    }

    ret = wl_cfg80211_scan_stop(cfg, wdev);
    if (unlikely(ret < 0)) {
        CFGP2P_ERR(("P2P scan stop failed, ret=%d\n", ret));
    }

    if (!p2p_is_on(cfg)) {
        return;
    }

#ifdef P2P_LISTEN_OFFLOADING
    wl_cfg80211_p2plo_deinit(cfg);
#endif /* P2P_LISTEN_OFFLOADING */

    /* Cancel any on-going listen */
    wl_cfgp2p_cancel_listen(cfg, bcmcfg_to_prmry_ndev(cfg), wdev, TRUE);

    ret = wl_cfgp2p_disable_discovery(cfg);
    if (unlikely(ret < 0)) {
        CFGP2P_ERR(("P2P disable discovery failed, ret=%d\n", ret));
    }

    p2p_on(cfg) = false;

    CFGP2P_DBG(("Exit. P2P interface stopped\n"));

    return;
}

int wl_cfgp2p_del_p2p_disc_if(struct wireless_dev *wdev,
                              struct bcm_cfg80211 *cfg)
{
    bool rollback_lock = false;

    if (!wdev || !cfg) {
        WL_ERR(("wdev or cfg is NULL\n"));
        return -EINVAL;
    }

    WL_INFORM(("Enter\n"));

    if (!cfg->p2p_wdev) {
        WL_ERR(("Already deleted p2p_wdev\n"));
        return -EINVAL;
    }

    if (!rtnl_is_locked()) {
        rtnl_lock();
        rollback_lock = true;
    }

#ifndef CONFIG_AP6XXX_WIFI6_HDF
    cfg80211_unregister_wdev(wdev);
#endif

    if (rollback_lock) {
        rtnl_unlock();
    }

    synchronize_rcu();

    MFREE(cfg->osh, wdev, sizeof(*wdev));

    cfg->p2p_wdev = NULL;

    CFGP2P_ERR(("P2P interface unregistered\n"));
    if (p2p_debug_ndev != NULL) {
        dhd_del_monitor(p2p_debug_ndev);
    }

    return 0;
}
#endif /* WL_CFG80211_P2P_DEV_IF */

void wl_cfgp2p_need_wait_actfrmae(struct bcm_cfg80211 *cfg, void *frame,
                                  u32 frame_len, bool tx)
{
    wifi_p2p_pub_act_frame_t *pact_frm;
    int status = 0;

    if (!frame ||
        (frame_len < (sizeof(*pact_frm) + WL_P2P_AF_STATUS_OFFSET - 1))) {
        return;
    }

    if (wl_cfgp2p_is_pub_action(frame, frame_len)) {
        pact_frm = (wifi_p2p_pub_act_frame_t *)frame;
        if (pact_frm->subtype == P2P_PAF_GON_RSP && tx) {
            CFGP2P_ACTION(
                ("Check TX P2P Group Owner Negotiation Rsp Frame status\n"));
            status = pact_frm->elts[WL_P2P_AF_STATUS_OFFSET];
            if (status) {
                cfg->need_wait_afrx = false;
                return;
            }
        }
    }

    cfg->need_wait_afrx = true;
    return;
}

int wl_cfgp2p_is_p2p_specific_scan(struct cfg80211_scan_request *request)
{
    if (request && (request->n_ssids == 1) && (request->n_channels == 1) &&
        IS_P2P_SSID(request->ssids[0].ssid, WL_P2P_WILDCARD_SSID_LEN) &&
        (request->ssids[0].ssid_len > WL_P2P_WILDCARD_SSID_LEN)) {
        return true;
    }
    return false;
}
